介紹
Lambda表達(dá)式是一種很簡單的方法,去定義一個(gè)匿名函數(shù)。Lambda是非常有用
的,因?yàn)樗鼈儽苊馕覀內(nèi)懸恍┌四承┖瘮?shù)的抽象類或者接口,然后在類中去
實(shí)現(xiàn)它們。在Kotlin,我們把一個(gè)函數(shù)作為另一個(gè)函數(shù)的參數(shù)。
簡化setOnClickListener()
我們用Android中非常典型的例子去解釋它是怎么工作
的: View.setOnClickListener() 方法。如果我們想用Java的方式去增加點(diǎn)擊
事件的回調(diào),我首先要編寫一個(gè) OnClickListener 接口:
public interface OnClickListener {
void onClick(View v);
}
然后我們要編寫一個(gè)匿名內(nèi)部類去實(shí)現(xiàn)這個(gè)接口:
view.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "Click", Toast.LENGTH_SHORT).show();
}
})
我們將把上面的代碼轉(zhuǎn)換成Kotlin(使用了Anko的toast函數(shù)):
view.setOnClickListener(object : OnClickListener {
override fun onClick(v: View) {
toast("Click")
}
}
很幸運(yùn)的是,Kotlin允許Java庫的一些優(yōu)化,Interface中包含單個(gè)函數(shù)可以被替代
為一個(gè)函數(shù)。如果我們這么去定義了,它會(huì)正常執(zhí)行:
fun setOnClickListener(listener: (View) -> Unit)
一個(gè)lambda表達(dá)式通過參數(shù)的形式被定義在箭頭的左邊(被圓括號(hào)包圍),然后在
箭頭的右邊返回結(jié)果值。在這個(gè)例子中,我們接收一個(gè)View,然后返回一個(gè)
Unit(沒有東西)。所以根據(jù)這種思想,我們可以把前面的代碼簡化成這樣:
view.setOnClickListener({ view -> toast("Click")})
這是非常棒的簡化!當(dāng)我們定義了一個(gè)方法,我們必須使用大括號(hào)包圍,然后在箭
頭的左邊指定參數(shù),在箭頭的右邊返回函數(shù)執(zhí)行的結(jié)果。如果左邊的參數(shù)沒有使用
到,我們甚至可以省略左邊的參數(shù):
view.setOnClickListener({ toast("Click") })
如果這個(gè)函數(shù)的最后一個(gè)參數(shù)是一個(gè)函數(shù),我們可以把這個(gè)函數(shù)移動(dòng)到圓括號(hào)外
面:
view.setOnClickListener() { toast("Click") }
并且,最后,如果這個(gè)函數(shù)只有一個(gè)參數(shù),我們可以省略這個(gè)圓括號(hào):
view.setOnClickListener { toast("Click") }
比原始的Java代碼簡短了5倍多,并且更加容易理解它所做的事情。非常讓人影響
深刻。
擴(kuò)展語言
多虧這些改變,我們可以去創(chuàng)建自己的 builder 和代碼塊。我們已經(jīng)在使用一些
有趣的函數(shù),比如 with 。如下簡單的實(shí)現(xiàn):
inline fun <T> with(t: T, body: T.() -> Unit) { t.body() }
這個(gè)函數(shù)接收一個(gè) T 類型的對(duì)象和一個(gè)被作為擴(kuò)展函數(shù)的函數(shù)。它的實(shí)現(xiàn)僅僅是
讓這個(gè)對(duì)象去執(zhí)行這個(gè)函數(shù)。因?yàn)榈诙€(gè)參數(shù)是一個(gè)函數(shù),所以我們可以把它放在
圓括號(hào)外面,所以我們可以創(chuàng)建一個(gè)代碼塊,在這這個(gè)代碼塊中我們可以使
用 this 和直接訪問所有的public的方法和屬性:
with(forecast) {
Picasso.with(itemView.ctx).load(iconUrl).into(iconView)
dateView.text = date
descriptionView.text = description
maxTemperatureView.text = "$high"
minTemperatureView.text = "$low"
itemView.setOnClickListener { itemClick(this) }
}
內(nèi)聯(lián)函數(shù)
內(nèi)聯(lián)函數(shù)與普通的函數(shù)有點(diǎn)不同。一個(gè)內(nèi)聯(lián)函數(shù)會(huì)在編譯的時(shí)候被替換
掉,而不是真正的方法調(diào)用。這在一些情況下可以減少內(nèi)存分配和運(yùn)行時(shí)
開銷。舉個(gè)例子,如果我們有一個(gè)函數(shù),只接收一個(gè)函數(shù)作為它的參數(shù)。
如果是一個(gè)普通的函數(shù),內(nèi)部會(huì)創(chuàng)建一個(gè)含有那個(gè)函數(shù)的對(duì)象。另一方
面,內(nèi)聯(lián)函數(shù)會(huì)把我們調(diào)用這個(gè)函數(shù)的地方替換掉,所以它不需要為此生
成一個(gè)內(nèi)部的對(duì)象。
另一個(gè)例子:我們可以創(chuàng)建代碼塊只提供 Lollipop 或者更高版本來執(zhí)行:
inline fun supportsLollipop(code: () -> Unit) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
code()
}
}
它只是檢查版本,然后如果滿足條件則去執(zhí)行?,F(xiàn)在我們可以這么做:
supportsLollipop {
window.setStatusBarColor(Color.BLACK)
}