Kotlin之心路歷程

Kotlin之心路歷程

以小白眼光看kotlin前端

kotlin這個小丫環被谷歌扶正爲大房兩年,期間看過很多博文,不少人也已經把我的項目遷移到kotlin了,固然國外的開發者更給力,直接大部企業開發已經kotlin,也訂閱了濤哥的極客時間(一直沒時間看,果真看視頻太費事,仍是文章能夠抽得時間擠一擠學),一直是不想學習啊,一我的的惰性就是這樣,java又不是不能用,代碼通俗易懂。說句內心話,java8的lambda表達式我都沒學,ps:不過好像用java8 lambda表達示的在工做中也沒怎麼碰到。。java

第一眼看到kotlin,大部分人都是,哇,我被它驚豔了(至少表面上都這麼說的),然而本渣狗並無感受(難道就我一個菜狗?),反而以爲:啥?爲了一個空安全學新的知識,if(x!=null)它不香哦。直到最近換了工做,遺留代碼一半是kotlin,糟了,是心動的感受。(不得不上手寫了)python

其實最開始上手依然痛苦無比,lambda表達式各類省略,函數是一等公民這句廢話幾乎每一個博客都寫了,可是我看着太抽象啊,我管你幾等公民,你告訴我它區別在哪啊,有啥用啊。android

無非下面一些問題

  1. 空安全並無讓我香到學習新知識安全

    空安全確實沒有香到非學新東西不可,不過,它的設計思路仍是不錯的。判空無非兩種作法,a. 在編寫代碼裏運行時避免空指針異常,也就是經常使用的if(x!=null);b. 直接靜態代碼檢查,在編譯期就避免了空指針,kotlin其實就是這種了,好比定義爲String,那麼很差意思,別的地方你傳個null,編譯器就給你整紅,非要可空,那後果你本身負,用String?吧。這樣的好處是省略了大把邏輯判斷,讓編譯器幫咱們把把關。app

  2. Lambda表達式省略一時爽,不會寫的人看不懂異步

    最簡單的也是出現最多的,莫過於點擊事件了jvm

    //kotlin
    view.setOnClickListener {
    	//TODO
    }
    
    //java
    view.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 
             }
         });
    複製代碼

    你們其實看不習慣的緣由就在於,沒有()帶參數,那我{}裏要使用()傳遞過來的參數怎麼辦,其實編譯器如今已經很智能的告訴咱們了 async

    咱們在{}中能夠經過it就能引用這個傳遞過來的view參數。至於Lambda表達式爲何能這麼省略,咱們到後面再講。

  3. 函數是一等公民究竟是個啥 無數次出現的函數是一等公民,到底講的是啥?其實就一句話,函數能夠作參數傳遞給變量不就得了。。。熟悉前端語言的其實應該很熟。ide

  4. 代碼中無端出現+-等運算符 運算符重載在不少lib中能看到,像常常用的協程庫就有

    public fun MainScope(): CoroutineScope = ContextScope(SupervisorJob() + Dispatchers.Main)
    複製代碼

    咱們本身寫的話,能夠這麼寫

    fun main() {
        println(Foo(1, 2) + Foo(3, 4))
    }
    
    data class Foo(val x: Int, val y: Int) {
        operator fun plus(other: Foo): Foo = Foo(x + other.x, y + other.y)
    }
    複製代碼

    運算符不少,能夠參考https://www.jianshu.com/p/d445209091f0

  5. 炫技的中綴表達式 炫技的大哥們挺喜歡這麼寫

println("I" am "OK")
複製代碼

或者一段中文style

println("你""幹嗎")
複製代碼

這都是啥鬼喲,看看源碼其實就好懂了,這就是一個簡單的語法糖

infix fun String.am(any: Any) {
}
infix fun String.在(any: Any) {
}
複製代碼

其實就是infix來修飾的中綴表達式,擴展了String,增長am方法,就是這麼一回事而已,擴展函數搞不明白,後面會講到(這是我最喜歡kotlin的一點了)

  1. DSL浪用 舉例?要我這麼懶的人來舉例,怕是擡舉我了,在網上找一個,
showDialog {
    title = "title"
    message = "message"
    rightClicks {
        toast("clicked!")
    }
}
複製代碼

嗯嗯,這配置代碼確實寫得不多簡潔了,可是對初學是一眼萬年(懵),dsl的動態性和高擴展性,讓kotlin有了更多的活力,可是DSL是要精而專,若是不到位的話,寫起來也沒輕鬆多少,固然,作公共組件,好比在這裏使用dialog的話,當你熟悉了的話,調用會快得多。

其實DSL說穿了就是擴展函數搞起,就那回事,主要是你要想得全,封裝得好

inline fun AppCompatActivity.showDialog(settings: CustomDialogFragment.() -> Unit) : CustomDialogFragment {
    val dialog = CustomDialogFragment.newInstance()
    dialog.apply(settings)
    val ft = this.supportFragmentManager.beginTransaction()
    val prev = this.supportFragmentManager.findFragmentByTag("dialog")
    if (prev != null) {
        ft.remove(prev)
    }
    ft.addToBackStack(null)
    dialog.show(ft, "dialog")
    return dialog
}
複製代碼

上面這段代碼擴展了Activity,因此咱們能夠直接調用showDialog函數,裏面惟一傳入的參數是個lambda表達式,因此能夠把{}直接放後面,別的都省略(lambda表達式後面立刻就會講)。showDialog( )方法中只有惟一的參數settings,其類型是CustomDialogFragment.() -> Unit,即帶有CustomDialogFragment參數類型的函數。

在showDialog( )方法內部,構造了CustomDialogFragment對象,並調用dialog.apply(settings)方法,其做用即在構造Dialog對象後,對Dialog進行設置。在實際調用showDialog()方法時,就能夠持有該CustomDialogFragment對象,而後調用CustomDialogFragment提供的public接口配置Dialog。

說一說香在哪

若是有朋友聽我嘍嗖到這裏的話,那麼說明咱們都同樣,是時候上硬菜了。

擴展庫——協程

別給我整有的沒有,我最開始香起來還就是協程了(手動滑稽)。

其實協程並非一個新概念,不少如今語言都是有滴,像python(人生苦短,我用python),go這些現代語言,都是有協程的,從計算機的發展史來講,有可能協程的概念比線程還出來得要早。不過無所謂了,我也不當大歷史家,如今咱們使用的協程通常是在線程上的,雖然協程也能夠直接運行在進程上,和線程毛關係都沒有,可是咱們作Android的,天生就有一條主線程。

不少人使用Rxjava看重的是啥?就是rxjava切換線程,順滑如絲,那麼,你若是隻使用了rxjava的這一點特性的話,協程就足夠了,性能還能直接提高很多。

概念太多,無非就是協程實際上就是極大程度的複用線程,經過讓線程滿載運行,達到最大程度的利用CPU,進而提高應用性能。咱們知道,線程阻塞時,其實這條線程是閒置的,經過協程,咱們能夠在同一線程中運行多個協程任務,當這個協程任務掛起時,執行另外的協程,這樣,線程就拉滿了。

廢話太多了,一哈講不清,雖然有考慮後面會單獨拎出來說,可是好歹要給口糖,說下簡單使用。

由於是擴展庫,因此須要手動引入

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
複製代碼
GlobalScope.launch(Dispatchers.Default) {
    println(Thread.currentThread().name)
    withContext(Dispatchers.Main) {
        println(Thread.currentThread().name)
    }
}

//運行結果
I/System.out: DefaultDispatcher-worker-1
I/System.out: main
複製代碼

這樣就線程切換了,若是要在主線程等異步線程工做完後拿到它的返回值呢

GlobalScope.launch(Dispatchers.Main) {
    val job = async(Dispatchers.Default) {
        println(Thread.currentThread().name)
        "a"
    }
    println(job.await())
}

//控制檯打印
I/System.out: DefaultDispatcher-worker-1
I/System.out: a
複製代碼

ps:不推薦使用GlobalScope,可使用MainScope,至於緣由以及使用方法,之前再講

一樣,不一樣協程前傳遞數據,也是可使用傳遞數據的,提供了channel很容易創建一個生產者——消費者模型

runBlocking {
        val channel = Channel<Int>(2)
        GlobalScope.launch {
            for (i in 1..5) {
                channel.send(i)
            }
            channel.close()
        }
        for (a in channel) {
            println(a)
        }
        println("done")
    }
複製代碼

使用runBlocking,是由於必須channel運行在一個掛起suspend函數或者協程裏,Channel<Int>(2)參數2是一次發送兩,默認是一個一個發。

協程先就只講這麼多了,先看下別的香法吧

擴展函數

擴展函數香到什麼地步,反正我是沒法抗拒了。試問寫java的童子們,曾幾什麼時候不都想擴展系統或者第三方類的方法時,只能含淚util,那麼,有了擴展函數的特性後,媽媽不再用擔憂我不能好好裝B了。

inline fun String.lastChar(): Char = this[this.length - 1]

fun main(){
   println("abc".lastChar())
}

//////
// 輸出c
複製代碼

就是由於擴展函數的天馬行空,纔有了kotlin的自由自在,這時候我想到的是安迪在肖申克的高牆上,看着大夥享受着本身贏來的啤酒,這是自由的味道~

動態代理

什麼,一句話就搞掂了動態代理?沒錯,就辣麼輕鬆愜意

interface Base{
    fun abc()
}
class BaseImpl(val x:Int):Base{
    override fun abc() {
        print(x)
    }
}

class Derived(b:Base):Base by b

fun main(){
    val b = BaseImpl(110)
    Derived(b).abc()
}

// 控制檯打印輸出
// 110
複製代碼

其中class Derived(b:Base):Base by b就幫咱們實現了動態代理

若是有興趣能夠反編譯一下看java代碼,其實kotlin是在編譯裏,會把咱們的代碼翻譯成靜態代理的代碼,由於沒有使用反射,要比jdk動態代理的效率高得多,並且,這樣的代碼寫起來不香麼?

真泛型

看到真泛型時,腦殼瓜子是否是嗡嗡的,其實咱們在java中使用泛型時,是無法拿到這個泛型的類型的,JVM的泛型都是經過類型擦除來實現的,也就是說泛型類實例的實參在編譯時被擦除,運行時不會被保留。kotlin運行在jvm上,同樣會遇到這個問題,因此kotlin想了一個好方法,並解決了這個問題。

fun main(){
    testT<String>()
} 

inline fun <reified T> testT(){
    println(T::class.java)
}

////class java.lang.String
複製代碼

能夠看到,咱們直接拿到了傳遞進來的泛型類型,像Gson這些庫,咱們就徹底能夠從新封裝一下了。

Lambda表達式

lambda是一個函數表達式,是匿名函數,用來表示函數類型的實例的一種方式。

這裏要先了解一下高階函數

來了來了,在kotlin中,函數是一等公民。

高階函數是將函數用做參數或返回值的函數。

而lambda常常用在此處

Lambda 的語法規則是這樣的:

  • lambda 表達式老是括在花括號中
  • 完整語法形式的參數聲明放在花括號內,多參數的話用逗號隔開
  • 函數體放在 -> 以後
  • 若是須要返回值,那麼函數體的最後一個表達式會被視爲返回值

以以下幾條規則可以讓 Lambda 的表示更加簡潔:

  • 若是參數類型能被推導出來,那麼能夠省略的類型標註

  • 若是 lambda 表達式的參數未使用,那麼能夠用下劃線取代其名稱

  • 若是 Lambda 只有一個參數而且編譯器能識別出類型,那麼能夠不用聲明這個參數並忽略 ->。 該參數會隱式聲明爲 it 。

  • 若是函數的最後一個參數接受函數,那麼做爲相應參數傳入的 lambda 表達式能夠放在圓括號以外

  • 若是該 lambda 表達式是調用時惟一的參數,那麼圓括號能夠徹底省略。

    仍是拿view的點擊事件來舉例,用kotlin的完整寫法是:

    view.setOnClickListener(object : View.OnClickListener {
        override fun onClick(v: View?) {
            //TODO
        }
    })
    複製代碼

    根據java中只有單個非默認抽象接口,在kotlin可使用函數來表示,這時,能夠精簡爲:

    view.setOnClickListener({
        v: View -> //TODO
    })
    複製代碼

    而lambda中惟一參數能夠省略

    view.setOnClickListener({
       //TODO
    })
    複製代碼

    lambda表達式爲惟一參數時,()能夠省略,這樣就變成了

    view.setOnClickListener{
       //TODO
    }
    複製代碼

    all啦

    其實lambda表達示只是在高階函數中使用時,省略比較多咱們用不習慣而已,習慣以爲挺爽的,不用再多寫一堆代碼了

小結

總的來講,kotlin是越用越香的那種,最開始從java寫kotlin還真的曲線陡峭,罵罵娘不就行了,世間萬物,難逃真香定律。


個人CSDN

下面是個人公衆號,歡迎你們關注我

相關文章
相關標籤/搜索