Kotlin基礎知識(九)——使用Java函數式接口

  • Java示例
public interface OnClickListener {
        void onClick(View v);
}
複製代碼
  • Kotlin示例
public interface OnClickListener { view -> ... }
複製代碼

這種方式能夠工做的緣由是OnClickListener接口只有一個抽象方法。這種接口被稱爲函數式接口,或者***SAM接口***,SAM表明單抽象方法,Java API 中隨處可見像RunnableCallable這樣的函數式接口,以及支持它們的方法。markdown

1、把lambda看成參數傳遞給Java方法

能夠把lambda傳給任何指望函數式接口的方法。ide

  • 示例1、有一個Runnable類型的參數的方法:
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

2、SAM構造方法:顯式地把lambda轉換成函數式接口

SAM構造方法是編譯器生成的函數,讓你執行從lambda到函數式接口實例的顯式轉換。能夠在編譯器不會自動應用轉換的上下文中使用它。對象

例如,若是有一個方法返回的是一個函數式接口的實例,不能直接返回一個lambda,要用SAM構造方法把它保證起來。

  • 使用SAM構造方法來返回值
// 定義
fun createAllDoneRunnable(): Runnable {
    return Runnable { println("All done!") }
}

// 測試
>>> createAllDoneRunnable().run()
All done!
複製代碼

SAM構造方法的名稱和底層函數式接口的名稱同樣。SAM構造方法只接受一個餐宿——一個被用做函數式接口單抽象方法體的lambda——並返回實現了這個接口的類的一個實例。

Lambda和添加 / 移除監聽器

注意lambda內部沒有匿名類對象那樣的this:沒有辦法引用在lambda轉換成的匿名類的實例。從編譯器的角度來看,lambda是一個代碼塊,不是一個對象,並且也不能把它當成對象引用。Lambda中的this引用指向的是包圍它的類。

相關文章
相關標籤/搜索