kotlin學(xué)習(xí)筆記

kotlin基礎(chǔ)知識

基礎(chǔ)語法

kotlin中的特殊語法

  1. object
    創(chuàng)建匿名內(nèi)部類的寫法
    kotlin中聲明單例的寫法
    object修飾的類是單例類
    object Test{
        fun getData(s :String):String{
            return s
        }
    }

    fun main(args: Array<String>) {
        Test.getData("")
    }
  1. String!
    只會在java和kotlin互調(diào)的時(shí)候出現(xiàn):編譯器認(rèn)為是一個(gè)java類型卻用在了kotlin上
  2. Unit
  • 沒有返回值的函數(shù)的隱藏類型
  • s函數(shù)作為參數(shù)時(shí)需要顯示注明

kotlin中的基礎(chǔ)說明

  1. kotlin中的Class不是編譯為class, 而是KClass
  2. kotlin中沒有封裝類
    java中的封裝類會被轉(zhuǎn)換為基本數(shù)據(jù)類型
  3. Kotlin類型空值敏感
  4. 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)

  1. kotlin中接受一個(gè)java對象的時(shí)候,如果你不能確定這個(gè)對象是否為空,一定需要將kotlin的接受對象設(shè)為可空類型的,否則會報(bào)空指針異常

函數(shù)與Lambda閉包

基礎(chǔ)

  1. 函數(shù)的特性語法
  • 支持默認(rèn)參數(shù):減少重載函數(shù)的數(shù)量
  • 函數(shù)體只有一個(gè)語句可以直接賦值給這個(gè)函數(shù)
    fun echo(name:String) = prinln("$name")
  1. 嵌套函數(shù)
  • 與內(nèi)部類類似,內(nèi)部函數(shù)可以訪問外部函數(shù)的局部變量
  • 用于:在某些條件下觸發(fā)遞歸的函數(shù) or 不希望被外部函數(shù)訪問到的函數(shù)
  • 不推薦
  1. 擴(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
  • 用于對第三方或無法控制的類新增需要用到的方法
  1. 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;
  1. 高階函數(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ù)聲明
  1. 內(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ù)

類與對象

  1. 構(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ù)
  1. 訪問修飾符
    private protected public internal
  • internal 一個(gè)模塊的類可以訪問 跨模塊的不可以 用于項(xiàng)目的結(jié)構(gòu)化擴(kuò)展
  1. 伴生對象
  • 一個(gè)類的伴生對象只能有一個(gè)
    class StringUtils {
        companion object{
            fun isEmpty(str: String):Boolean{
                return str == ""
            }
        }
    }
    fun main(args: Array<String>) {
        StringUtils.isEmpty("")
    }
    //Java
    StringUtils.Companion.isEmpty("");
  1. 單例類
    //單例的寫法 推薦
    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()
    }
  1. 動態(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()
    }
  1. 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中的高級特性

  1. 解構(gòu)
    將類拆解并分別賦值
    常用于遍歷map
    var map = mapOf<String, String>("key" to "key", "value" to "value")
    for((k,v) in map){
        println("$k ----- $v")
    }
  1. 循環(huán)與集合操作符
    var list = arrayListOf<Char>('a','b','c')
    val a = list.map { it - 'a' }.filter { it > 0 }.find{it > 1}
    println(a)
  1. 運(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)算符有上線

  2. 作用域函數(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"
        }
    }
  1. 中綴表達(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 "等于"
            }
        }
    }
  1. 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è)類上
    可以用在跨平臺上,提供兼容性
  2. DSL
    Domain Specific Language
    領(lǐng)域?qū)S谜Z言
    . 提高開發(fā)效率 減小溝通成本
    . Lambda 高階函數(shù) 擴(kuò)展函數(shù) 運(yùn)算符重載 中綴表達(dá)式
  3. 總結(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ā)空指針異常

  1. 每次引用對象的時(shí)候,都去進(jìn)行空對象判空,在運(yùn)行期避免對象空指針
  2. 通過靜態(tài)代碼檢查,編譯插件檢查,在編譯期避免空指針異常
    kotlin是以上兩種方式的結(jié)合
內(nèi)聯(lián)的特殊情況
  1. 在kotlin中,內(nèi)部Lambda是不允許中斷外部函數(shù)執(zhí)行的
  2. inline的Lambda可以中段外部函數(shù)調(diào)用
  3. croossinline不允許inline的Lambda中斷外部函數(shù)執(zhí)行
  4. noinline拒絕內(nèi)聯(lián) 通常用于修飾一個(gè)返回函數(shù)為內(nèi)聯(lián)函數(shù)的時(shí)候
Kotlin的真泛型與實(shí)現(xiàn)方法
  1. 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)
    }
  1. 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)注解

  1. @JvmOverloads: 在有默認(rèn)參數(shù)值的方法中使用@JvmOverloads注解,則Kotlin就會暴露多個(gè)重載方法。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容