public interface OnClickListener {
void onClick(View v);
}
複製代碼
public interface OnClickListener { view -> ... }
複製代碼
這種方式能夠工做的緣由是OnClickListener接口只有一個抽象方法。這種接口被稱爲函數式接口,或者***SAM
接口***,SAM
表明單抽象方法,Java API 中隨處可見像Runnable
和Callable
這樣的函數式接口,以及支持它們的方法。markdown
能夠把lambda傳給任何指望函數式接口的方法。ide
void postponeComputation(int delay, Runnable computation) 複製代碼
在Kotlin中,能夠調動用它並把一個lambda做爲實參傳給它。編譯器會自動把它轉換成一個Runnable的實例:函數
postponeComputation(1000) { println(42) }
複製代碼
注意,「一個
Runnable
的實例」,指的是「一個實現了Runnable
接口的匿名類的實例」。編譯器會幫助你建立它,並使用lambda
做爲單抽象方法——這個例子中是run
方法——的方法體。post
經過顯式地建立一個實現了Runnable的匿名對象也能達到一樣的效果:測試
// 把對象表達式做爲函數式接口的實現傳遞
postpostComputation(1000, object: Runnable {
override fun run() {
println(42)
}
})
複製代碼
注意:this
- 在顯式地聲明對象時,每次調用都會建立一個新的實例。
- 使用lambda的狀況不一樣:若是lambda沒有訪問任何來自自定義它的函數的變量,相應的匿名類實例能夠在屢次調用之間重用:
// 整個程序只會建立一個Runnable的實例 postponeComputation(1000) { println(42) } 複製代碼
所以,徹底等價的實現應該是下面這段代碼中的顯式object聲明,它把Runnable實例存儲在一個變量中,而且每次調用的時候都使用這個變量:spa
// 編譯成全局變量;程序中僅此一個實例
val runnable = Runnable { println(42) }
fun handleComputation() {
// 每次postponeComputation調用時用的是一個對象
postponeComputation(1000, runnable)
}
複製代碼
若是lambda從包圍它的做用域中捕獲了變量,每次調用就再也不可能重用同一個實例了。這種狀況下,每次調用時編譯器都會建立一個新對象,其中存儲着唄捕獲的變量的值。code
// lambda會捕獲「id」這個變量
fun handleComputation(id: String) {
// 每次handleComputation調用時都建立一個Runnable的新實例
postponeComputation(1000) { println(id) }
}
複製代碼
注意:這裏討論的爲lambda建立一個匿名類,以及該類的實例的方法只對指望函數式接口的Java方法有效,可是對集合使用Kotlin擴展方法的方式並不適用。若是把lambda傳給了標記成***
inline
的Kotlin函數,是不會建立任何匿名類的。而大多數的庫函數都標成了inline
***。orm
SAM構造方法是編譯器生成的函數,讓你執行從lambda到函數式接口實例的顯式轉換。能夠在編譯器不會自動應用轉換的上下文中使用它。對象
例如,若是有一個方法返回的是一個函數式接口的實例,不能直接返回一個lambda,要用SAM構造方法把它保證起來。
// 定義
fun createAllDoneRunnable(): Runnable {
return Runnable { println("All done!") }
}
// 測試
>>> createAllDoneRunnable().run()
All done!
複製代碼
SAM構造方法的名稱和底層函數式接口的名稱同樣。SAM構造方法只接受一個餐宿——一個被用做函數式接口單抽象方法體的lambda——並返回實現了這個接口的類的一個實例。
Lambda和添加 / 移除監聽器
注意lambda內部沒有匿名類對象那樣的this:沒有辦法引用在lambda轉換成的匿名類的實例。從編譯器的角度來看,lambda是一個代碼塊,不是一個對象,並且也不能把它當成對象引用。Lambda中的this引用指向的是包圍它的類。