kotlin基礎(chǔ)知識
基礎(chǔ)語法
kotlin中的特殊語法
- object
創(chuàng)建匿名內(nèi)部類的寫法
kotlin中聲明單例的寫法
object修飾的類是單例類
object Test{
fun getData(s :String):String{
return s
}
}
fun main(args: Array<String>) {
Test.getData("")
}
- String!
只會在java和kotlin互調(diào)的時(shí)候出現(xiàn):編譯器認(rèn)為是一個(gè)java類型卻用在了kotlin上 - Unit
- 沒有返回值的函數(shù)的隱藏類型
- s函數(shù)作為參數(shù)時(shí)需要顯示注明
kotlin中的基礎(chǔ)說明
- kotlin中的Class不是編譯為class, 而是KClass
- kotlin中沒有封裝類
java中的封裝類會被轉(zhuǎn)換為基本數(shù)據(jù)類型 - Kotlin類型空值敏感
- Kotlin中沒有靜態(tài)變量和靜態(tài)方法
object KotlinObject{ /** * 不聲明 : KotlinObject.INSTANCE.sayMessage("s"); * 聲明 : KotlinObject.sayMessage("s"); */ @JvmStatic fun sayMessage(string: String){ println(string) } }
java和kotlin中互相調(diào)用的注意點(diǎn)
- kotlin中接受一個(gè)java對象的時(shí)候,如果你不能確定這個(gè)對象是否為空,一定需要將kotlin的接受對象設(shè)為可空類型的,否則會報(bào)空指針異常
函數(shù)與Lambda閉包
基礎(chǔ)
- 函數(shù)的特性語法
- 支持默認(rèn)參數(shù):減少重載函數(shù)的數(shù)量
- 函數(shù)體只有一個(gè)語句可以直接賦值給這個(gè)函數(shù)
fun echo(name:String) = prinln("$name")
- 嵌套函數(shù)
- 與內(nèi)部類類似,內(nèi)部函數(shù)可以訪問外部函數(shù)的局部變量
- 用于:在某些條件下觸發(fā)遞歸的函數(shù) or 不希望被外部函數(shù)訪問到的函數(shù)
- 不推薦
- 擴(kuò)展函數(shù)
kotlin: 可以靜態(tài)的給一個(gè)類擴(kuò)展它的成員方法和成員變量, 不具備運(yùn)行時(shí)的多態(tài)效果
----kotlin------
public fun File.readText(charset: Charset = Charsets.UTF_8): String = readBytes().toString(charset)
----Java------
public class JavaMain {
public static void main(String[] args) {
File file = new File("readme.md");
System.out.println(FilesKt.readText(file, Charsets.UTF_8));
}
}
- 擴(kuò)展函數(shù)會被編譯為 public static final
- 用于對第三方或無法控制的類新增需要用到的方法
- Lambda閉包
- 如果lambda沒有參數(shù), 可以省略箭頭符號 ->
- 如果lambda是函數(shù)的最后一個(gè)參數(shù),可以將大括號放在小括號的外面
- 如果函數(shù)只有一個(gè)參數(shù)且這個(gè)參數(shù)是lambda,則可以省略小括號
//lambda閉包聲明
var name = {str: String -> println(str)}
fun main(args: Array<String>) {
name("name")
}
//編譯為
Function1<String, Unit> echo = (Function1)echo.INSTANCE;
- 高階函數(shù)
函數(shù)或者lambda的參數(shù)又是一個(gè)函數(shù)或lambda
fun onlyIf(isDebug: Boolean, block: () -> Unit){
if(isDebug) block()
}
fun main(args: Array<String>) {
onlyIf(true){
println("打印日志")
}
}
- 函數(shù)是“一等公民”, 可以用對象:: 方法名 引用一個(gè)函數(shù)聲明
- 內(nèi)聯(lián)函數(shù)
- Kotlin的Lambda是一個(gè)匿名對象,大量重復(fù)的lambda表達(dá)式,會生成很多臨時(shí)的無用對象
- inline 進(jìn)行修飾,這樣當(dāng)方法在編譯的時(shí)候就拆解方法的調(diào)用為語句調(diào)用,進(jìn)而減少創(chuàng)建不必要的對象
- 過度使用會造成編譯器的編譯負(fù)擔(dān),同時(shí)使代碼塊很龐大
- 一般僅用于修飾高階函數(shù)
類與對象
- 構(gòu)造函數(shù)
- Kotlin中默認(rèn)的類是public final的
- 主構(gòu)造函數(shù)和次構(gòu)造函數(shù)
一個(gè)類有多個(gè)構(gòu)造函數(shù),需要顯示聲明它的次級構(gòu)造函數(shù),次級構(gòu)造函數(shù)必須直接或間接的繼承主構(gòu)造函數(shù)或者父類的構(gòu)造函數(shù)
- 訪問修飾符
private protected public internal
- internal 一個(gè)模塊的類可以訪問 跨模塊的不可以 用于項(xiàng)目的結(jié)構(gòu)化擴(kuò)展
- 伴生對象
- 一個(gè)類的伴生對象只能有一個(gè)
class StringUtils {
companion object{
fun isEmpty(str: String):Boolean{
return str == ""
}
}
}
fun main(args: Array<String>) {
StringUtils.isEmpty("")
}
//Java
StringUtils.Companion.isEmpty("");
- 單例類
//單例的寫法 推薦
class Single private constructor(){
companion object{
fun get() : Single{
return Holder.instance
}
}
private object Holder{
val instance = Single()
}
}
fun main(args: Array<String>) {
val single = Single.get()
}
- 動態(tài)代理
- 在運(yùn)行時(shí)動態(tài)地對某些東西代理
- 在語言層面原生支持的動態(tài)代理:by
- kotlin會將動態(tài)代理編譯為靜態(tài)代理
- 比java的動態(tài)代理效率高
interface Animal{
fun bark()
}
class Dog : Animal{
override fun bark() {
println("wang wang")
}
}
class Zoo(animal: Animal): Animal by animal
fun main(args: Array<String>) {
Zoo(Dog()).bark()
}
- Kotlin中特有的類
- 數(shù)據(jù)類 data class 類名字 final 類型 不能再添加open方法 自動重寫toString hashCode equals copy方法
- 枚舉類:與java中的類似,但很少使用,用更強(qiáng)大密閉類代替
enum class Command{
A, B, C, D
}
fun exec(command: Command) = when(command){
Command.A -> {}
Command.B -> {}
Command.C -> {}
Command.D -> {}
}
- 密閉類 sealed class 可以有子類,但子類需要放在一個(gè)文件中,所以一般將子類寫在內(nèi)部。 密閉類可以有擴(kuò)展子類
sealed class SupperCommand{
object A: SupperCommand()
object B: SupperCommand()
object C: SupperCommand()
object D: SupperCommand()
//擴(kuò)展子類
class PACE(var paceL: Int) : SuperCommand()
}
fun exec(supperCommand: SupperCommand) = when(supperCommand){
SupperCommand.A -> println("A")
SupperCommand.B -> println("B")
SupperCommand.C -> println("C")
SupperCommand.D -> println("D")
is SupperCommand.PACE -> {}
}
fun main(args: Array<String>) {
exec(SupperCommand.A)
}
kotlin中的高級特性
- 解構(gòu)
將類拆解并分別賦值
常用于遍歷map
var map = mapOf<String, String>("key" to "key", "value" to "value")
for((k,v) in map){
println("$k ----- $v")
}
- 循環(huán)與集合操作符
var list = arrayListOf<Char>('a','b','c')
val a = list.map { it - 'a' }.filter { it > 0 }.find{it > 1}
println(a)
運(yùn)算符重載
. 通過operator關(guān)鍵字
. 修飾一個(gè)方法的時(shí)候,表示方法命指代一個(gè)運(yùn)算符
. operator:將一個(gè)函數(shù)標(biāo)記為重載一個(gè)操作符或者實(shí)現(xiàn)一個(gè)約定
. 一定是定義好的運(yùn)算符,不能憑空重載運(yùn)算符,運(yùn)算符有上線作用域函數(shù)
. kotlin內(nèi)置一系列可以對數(shù)據(jù)進(jìn)行變換的函數(shù),與集合操作符號相似,但集合操作符值只能用于集合的操作變換,而作用域函數(shù)可以用于所有對象
. run{...} with(T){...} let{...} apply{...} also{...}
fun main(args: Array<String>) {
val user = User("zhangsan")
//let 和 run 都會返回閉包的執(zhí)行結(jié)果,區(qū)別在于let有閉包參數(shù),run沒有閉包參數(shù)
var letResult= user.let{"let::${it.javaClass}"}
//lambda的特性,如果只有一個(gè)參數(shù)的時(shí)候,可以省略不寫,用it替代
var letResult2 = user.let{user : User -> "let::${user.javaClass}"}
println(letResult)
val runResult = user.run { "run::${this.javaClass}" }
println(runResult)
//also 和 apply都不返回閉包的執(zhí)行結(jié)果,區(qū)別在于also有閉包參數(shù),apply沒有
user.also {
println("also::${it.javaClass}")
}.apply {
println("apply::${this.javaClass}")
}.name = "hello"
//takeIf 的閉包返回一個(gè)判斷結(jié)果, 為false時(shí),takeIf函數(shù)會返回空
//takeUnless 與 takeIf 剛好相反,閉包的判斷結(jié)果, 為true時(shí)函數(shù)會返回空
user.takeIf { it.name.length > 0 } ?.also { println("姓名為${it.name}") } ?: print("姓名為空")
user.takeUnless { it.name.length > 0 } ?.also { println("姓名為空") } ?: print("姓名為${user.name}")
//重復(fù)執(zhí)行當(dāng)前閉包
repeat(5){
println(user.name)
println(it)
}
//with比較特殊,不是以擴(kuò)展方法的形式存在,而是一個(gè)頂級函數(shù)
with(user){
this.name = "with"
}
user.apply {
this.name = "with"
}
}
- 中綴表達(dá)式
. 和運(yùn)算符的重載一樣,本質(zhì)都是一個(gè)特殊的函數(shù),通過函數(shù)的調(diào)用完成
. 一個(gè)函數(shù)只有用于兩個(gè)角色類似的對象時(shí)才將其聲明為中綴函數(shù), 如果一個(gè)方法會改動其接受者,那么不要聲明為中綴形式。
. infix關(guān)鍵字
fun main(args: Array<String>) {
println(5 vs 6)
}
//Int. 表示函數(shù)的接收者
infix fun Int.vs(num: Int): CompareResult =
if(this - num < 0){
CompareResult.LESS
}else if(this - num > 0){
CompareResult.MORE
}else{
CompareResult.EQUAL
}
sealed class CompareResult{
object MORE: CompareResult(){
override fun toString(): String {
return "大于"
}
}
object LESS: CompareResult(){
override fun toString(): String {
return "小于"
}
}
object EQUAL: CompareResult(){
override fun toString(): String {
return "等于"
}
}
}
- kotlin中的特殊符號
. 反引號:解決關(guān)鍵字沖突問題 將一個(gè)不合法的字符變?yōu)楹戏ǖ?br> 不推薦使用
fun`1234`(){}
使用場景較?。罕热鏸nternal 只能用于kotlin中, java當(dāng)作public
如果某個(gè)類不希望被java訪問, 則可以將這個(gè)類做一些特殊不合法的字符
. == 等同于java的equals === 等同于java的 ==
. typealias
類似于c和c++中的def 將一個(gè)類映射到另一個(gè)類上
可以用在跨平臺上,提供兼容性 - DSL
Domain Specific Language
領(lǐng)域?qū)S谜Z言
. 提高開發(fā)效率 減小溝通成本
. Lambda 高階函數(shù) 擴(kuò)展函數(shù) 運(yùn)算符重載 中綴表達(dá)式 - 總結(jié)
[圖片上傳失敗...(image-bda9ca-1579446616919)]
[圖片上傳失敗...(image-62dc93-1579446616919)]
語法特性解析
變量、常量與只讀
. var和val最本質(zhì)的區(qū)別是val不能有setter,但val 可以通過重寫get方法達(dá)到改變它的值的效果
. 編譯時(shí)常量 const val a = 0 const只能修飾object的屬性 或 top-level變量 const變量的值必須在編譯期間確定下來,所以它的類型只能是String或基本類型
因?yàn)閷ο蟮闹翟诰幾g器是不確定的,會隨著在運(yùn)行時(shí)分配內(nèi)存位置不一致,導(dǎo)致對象不是一個(gè)固定的對象。
空安全是如何實(shí)現(xiàn)的
嘗試調(diào)用空對象的成員變量或方法會觸發(fā)空指針異常
- 每次引用對象的時(shí)候,都去進(jìn)行空對象判空,在運(yùn)行期避免對象空指針
- 通過靜態(tài)代碼檢查,編譯插件檢查,在編譯期避免空指針異常
kotlin是以上兩種方式的結(jié)合
內(nèi)聯(lián)的特殊情況
- 在kotlin中,內(nèi)部Lambda是不允許中斷外部函數(shù)執(zhí)行的
- inline的Lambda可以中段外部函數(shù)調(diào)用
- croossinline不允許inline的Lambda中斷外部函數(shù)執(zhí)行
- noinline拒絕內(nèi)聯(lián) 通常用于修飾一個(gè)返回函數(shù)為內(nèi)聯(lián)函數(shù)的時(shí)候
Kotlin的真泛型與實(shí)現(xiàn)方法
- kotlin的泛型支持限定泛型的參數(shù)類型,支持多個(gè)類型, java的泛型會在編譯時(shí)將泛型參數(shù)抹去,變?yōu)閛bject
class Test<T> where T : Callback, T : Runnalble{
fun add(t: T){
t.run()
t.callback()
}
}
//java
public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException{...}
//kotlin
//inline關(guān)鍵字不能省略 因?yàn)楸仨氃诰幾g時(shí)知道T的類型 reified表示T是一個(gè)真泛型
inline fun <reified T> Gson.fromJson(json: String): T{
return fromJson(json, T::class.java)
}
- reified表示T是一個(gè)真泛型, 只能修飾函數(shù),不能修飾類,所以類可以通過下面的方法在運(yùn)行時(shí)拿到泛型參數(shù),從而使得類也有真泛型
//android中實(shí)現(xiàn)MVP
fun main(args: Array<String>) {
val b = View<Presenter>().presenter
//等同于
val a = View.Companion.invoke<Presenter>().presenter
}
class View<T>(val clazz: Class<T>){
val presenter by lazy { clazz.newInstance() }
//伴生對象會在構(gòu)造函數(shù)調(diào)用之前,也就是類被加載到類加載器的時(shí)候創(chuàng)建好
companion object{
//重載了invoke操作符,同時(shí)調(diào)用了構(gòu)造函數(shù),并將當(dāng)前的泛型類型,傳遞給了view的構(gòu)造函數(shù),所以在運(yùn)行時(shí)可以拿到clazz運(yùn)行變量
inline operator fun <reified T> invoke() = View(T::class.java)
}
}
class Presenter{
override fun toString(): String {
return "Presenter"
}
}
協(xié)程
Kotlin中的相關(guān)注解
- @JvmOverloads: 在有默認(rèn)參數(shù)值的方法中使用@JvmOverloads注解,則Kotlin就會暴露多個(gè)重載方法。