Kotlin學習與實踐 (九)帶接收者的lambda及Java的函數式接口
阿新 • • 發佈:2018-01-30
參數 通過 需要 語法 end handle brush 捕捉 tin
帶接收者的lambda
* 在lambda 函數體內可以調用一個不同對象的方法,而且無須借助任何額外限定符;這種能力再Java中是找不到的。
* 這樣的lambda叫做“帶接收者的lambda”
先舉個普通函數作為反例:
fun alphabet(): String { val result = StringBuilder() for (letter in ‘A‘..‘Z‘) { result.append(letter) } result.append("\nNow ,I know the alphabet") return result.toString() }
在上面的例子中可以看到函數中對 result 對象反復的調用,如果反復調用的多了就會變得很糟,Kotlin 帶接受者的lambda就解決了這個問題。
首先看wtih函數:
* with 語法 是一個接收兩個參數的函數:嚴格的寫應該是 with(aa,{...lambda...}), 利用把lambda 作為最後一個參數可以放到括號外面的約定可以提高可讀性 with(xx){...lambda...}
* with函數把第一個參數 轉化成 作為第二個參數的lambda 的接收者,可以顯式地通過this 引用來訪問這個接收者,也可以省略this 引用直接訪問
fun alphabetWith(): String { val result = StringBuilder() return with(result) { //指定接收者的值,然後就可以在lambda中使用 for (letter in ‘A‘..‘Z‘) { this.append(letter) //通過this顯示的來調用接收者 } append("\nNow ,I know the alphabet")//也可以省掉this 來調用接收者 result.append("hahaha") this.toString() //從lambda中返回 } }
* 可以使用表達式函數體語法繼續簡化函數。
fun alpabetWithF() = with(StringBuilder()) { for (letter in ‘A‘..‘Z‘) { this.append(letter) //通過this顯示的來調用接收者 } append("\nNow ,I know the alphabet")//也可以省掉this 來調用接收者 append("hahaha") toString() //從lambda中返回 }
* with返回值是執行lambda代碼的結果,改結果就是lambda中的最後一個表達式的值。
* 如果你想返回的是接收者對象(傳入lambda的對象)而不是lambda執行的結果時候,apply函數就排上用場了。
* apply被聲明成一個擴展函數。它的接收者變成了作為實參的lambda的接收者。
* 執行apply的結果是StringBuilder,所以接下來你可以調用toString把它轉化成String。
fun alpabetApply() = StringBuilder().apply { for (letter in ‘A‘..‘Z‘) { this.append(letter) //通過this顯示的來調用接收者 } append("\nNow ,I know the alphabet")//也可以省掉this 來調用接收者 append("hahaha") }.toString()
* Kotlin中可以再任意對象上使用apply,不需要任何特別的支持
* apply 允許你使用緊湊的表達式函數體的風格
* lambda執行之後,apply返回已經初始化過的接收者實例
fun createViewWithCustomAttribites(context:Context)= TextView(context).apply{ text = "Simple Text" testSize = 20 setPadding(20,15,1,0) }
以上是Kotlin中最典型最基本的帶接受者的lambda函數,除了with apply之外還有其他的使用起來很贊的帶接受者的函數....
使用Java的函數式接口
* 在Kotlin中可以傳遞一個lambda 代替傳統的Java中的匿名類做實參
* 使用lambda代替Java匿名類的方式可以工作的原因是 ActionListener 接口中只有一個抽象方法。(Runnable、Callable)
* 這種接口被稱為函數式接口,或者SAM接口,SAM代表單抽象方法。
* Kotlin 允許你再調用接收函數式接口作為參數的方式時使用lambda,來保證代碼的整潔。
fun lambdaInnerClass() { // val btn = Button() // btn.setOnclickListener{v-> println("")} val btn = Button() btn.addActionListener { e -> println("hahaha") } }
下面來一個演示的例子,首先放出Java定義的函數:
public class JavaCallTest { public void postponeComputation(int delay, Runnable computation) { Thread thread = new Thread(computation); try { thread.join(delay); thread.start(); } catch (InterruptedException e) { e.printStackTrace(); } } }
* 在Koltin中使用lambda代替匿名類參數,編譯器會把最後打lambda 自動編譯轉換成一個Runnable實例傳遞給方法
val javaTest = JavaCallTest() fun testLambdaCallJava() { javaTest.postponeComputation(100) { println(42) } // 如下通過顯示創建匿名對象也能達到效果 javaTest.postponeComputation(1000, object : Runnable { override fun run() { println("452") } }) }
* 這裏有一點不一樣,當你顯式地聲明對象時,每次調用都會創建一個新的對象。
* 使用lambda的情況不同:如果lambda沒有訪問任何來自定義它的函數的變量,響應的匿名類對象可以在多次調用之間重用。
val runnable = Runnable { println(42) } fun reUsing() { javaTest.postponeComputation(1000, runnable) }
* 上面的runnable 使用lambda對應生成的對象就會多次復用 因為沒有沒有引用函數中定義的變量
* 如果lambda從包圍它的作用域中捕捉了變量,每次調用就不能再重復利用同一個實例了,這時每次就會創建一個新的對象,其中存儲著被捕獲的變量的值
* 如下:
fun handleComputation(id: String) { javaTest.postponeComputation(1000) { println(id) } }
SAM接口還有一些別的特性,暫時就不列舉出來了...
Kotlin學習與實踐 (九)帶接收者的lambda及Java的函數式接口