以小白眼光看kotlin前端
kotlin這個小丫環被谷歌扶正爲大房兩年,期間看過很多博文,不少人也已經把我的項目遷移到kotlin了,固然國外的開發者更給力,直接大部企業開發已經kotlin,也訂閱了濤哥的極客時間(一直沒時間看,果真看視頻太費事,仍是文章能夠抽得時間擠一擠學),一直是不想學習啊,一我的的惰性就是這樣,java又不是不能用,代碼通俗易懂。說句內心話,java8的lambda表達式我都沒學,ps:不過好像用java8 lambda表達示的在工做中也沒怎麼碰到。。java
第一眼看到kotlin,大部分人都是,哇,我被它驚豔了(至少表面上都這麼說的),然而本渣狗並無感受(難道就我一個菜狗?),反而以爲:啥?爲了一個空安全學新的知識,if(x!=null)它不香哦。直到最近換了工做,遺留代碼一半是kotlin,糟了,是心動的感受。(不得不上手寫了)python
其實最開始上手依然痛苦無比,lambda表達式各類省略,函數是一等公民這句廢話幾乎每一個博客都寫了,可是我看着太抽象啊,我管你幾等公民,你告訴我它區別在哪啊,有啥用啊。android
空安全並無讓我香到學習新知識安全
空安全確實沒有香到非學新東西不可,不過,它的設計思路仍是不錯的。判空無非兩種作法,a. 在編寫代碼裏運行時避免空指針異常,也就是經常使用的if(x!=null);b. 直接靜態代碼檢查,在編譯期就避免了空指針,kotlin其實就是這種了,好比定義爲String,那麼很差意思,別的地方你傳個null,編譯器就給你整紅,非要可空,那後果你本身負,用String?吧。這樣的好處是省略了大把邏輯判斷,讓編譯器幫咱們把把關。app
Lambda表達式省略一時爽,不會寫的人看不懂異步
最簡單的也是出現最多的,莫過於點擊事件了jvm
//kotlin
view.setOnClickListener {
//TODO
}
//java
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
複製代碼
你們其實看不習慣的緣由就在於,沒有()帶參數,那我{}裏要使用()傳遞過來的參數怎麼辦,其實編譯器如今已經很智能的告訴咱們了 async
函數是一等公民究竟是個啥 無數次出現的函數是一等公民,到底講的是啥?其實就一句話,函數能夠作參數傳遞給變量不就得了。。。熟悉前端語言的其實應該很熟。ide
代碼中無端出現+-等運算符 運算符重載在不少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
炫技的中綴表達式 炫技的大哥們挺喜歡這麼寫
println("I" am "OK")
複製代碼
或者一段中文style
println("你" 在 "幹嗎")
複製代碼
這都是啥鬼喲,看看源碼其實就好懂了,這就是一個簡單的語法糖
infix fun String.am(any: Any) {
}
infix fun String.在(any: Any) {
}
複製代碼
其實就是infix來修飾的中綴表達式,擴展了String,增長am方法,就是這麼一回事而已,擴展函數搞不明白,後面會講到(這是我最喜歡kotlin的一點了)
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是一個函數表達式,是匿名函數,用來表示函數類型的實例的一種方式。
這裏要先了解一下高階函數
來了來了,在kotlin中,函數是一等公民。
高階函數是將函數用做參數或返回值的函數。
而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還真的曲線陡峭,罵罵娘不就行了,世間萬物,難逃真香定律。
下面是個人公衆號,歡迎你們關注我