kotlin提供了高階函數這個概念,能夠在一些場景提升編碼效率java
1、什麼是高階函數bash
通俗的說和數學裏面的高階函數概念相似,也就是函數裏面的參數能夠是函數。固然返回值也能夠是函數。ide
2、kotlin高階函數使用場景分析函數
1.先看看平時使用比較多的內置高階函數 用kotlin寫view的onClickListenerui
tV.setOnClickListener {
//doSomeThing
}
複製代碼
裏面的lamba表達式就是一個函數編碼
不太形象?再看看集合裏面的filter、mapspa
listOf(1, 2, 3)
.filter { it > 2 }
.map { it + 5 }
/**
* Returns a list containing only elements matching the given [predicate].
*/
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
return filterTo(ArrayList<T>(), predicate)
}
複製代碼
filter、map的參數都是一個lambda函數code
2.高階函數有什麼用cdn
就拿filter函數來講,好比實現一個過濾的邏輯,判斷是符合的 若classA 和classB都須要調用這個函數,那麼函數就須要兼容這兩種狀況對象
fun filter(): Boolean {
if (classA) {
return true
} else if (classB) {
return false
}
return false
}
複製代碼
if else無可厚非,可是若是後面有classC classD...都須要考慮呢,這顯然違背了開閉原則。那麼天然是要面向抽象而不是具體,固然就是抽象類或者接口。
若用java的方式去實現,會變成這樣
interface IJudge {
fun canFilter(): Boolean
}
class ClassA : IJudge {
override fun canFilter(): Boolean {
return true
}
}
class ClassB : IJudge {
override fun canFilter(): Boolean {
return false
}
}
fun filter(a:Int,b:Int,jugde: IJudge): Boolean {
//加一些邏輯
return jugde.canFilter()
}
複製代碼
這個是硬傷,面向抽象就得加這麼接口,而後多寫一些代碼。
若用高階函數實現
fun filter(a: Int, b: Int, canFilter: (a:Int,b:Int) -> Boolean): Boolean {
//加一些邏輯
return canFilter(a,b)
}
//調用方1
filter(1, 2) { a: Int, b: Int ->
a * b > 10
}
//調用方2
filter(1, 2) { a: Int, b: Int ->
a + b < 5
}
複製代碼
這樣就省了個接口,後面分析實際是編譯器幫忙處理,其實仍是生成了接口
3、kotlin高階函數的實現
來看看kotlin編譯器是怎麼實現的吧 首先把上面那段kotlin代碼反編譯成java
kt:
fun filter(a: Int, b: Int, canFilter: (a:Int,b:Int) -> Boolean): Boolean {
//加一些邏輯
return canFilter(a,b)
}
java:
public final boolean filter(int a, int b, @NotNull Function2 canFilter) {
Intrinsics.checkParameterIsNotNull(canFilter, "canFilter");
canFilter.invoke(a, b);
return (Boolean)canFilter.invoke(a, b);
}
複製代碼
其實是kt內置的 Functions.kt
這裏因爲我傳的是2個參數的lambda函數,因此調用的是Function2那麼從這裏能得來上面結論:
a.高階函數所謂的能夠省略接口,其實只能省略只有一個方法的接口,由於function函數只有一個方法
b.上邊的fliter函數除了canFIlter(a,b)還能夠使用canFilter.invoke(a,b)調用。這個在須要對函數判空的時候頗有用。好比替換隻有一個方法的接口回調能夠callback?.invoke(a,b,c) , 由於callbck?(a,b,c)是不能編譯經過的。
c.雖然Functions.kt文件方法數是有限的,感受意味着lambda參數是有限的,最多22個參數,超過會編譯失敗。可是當真的超過期,會調用另一個FunctionN.kt
operator fun invoke(vararg args: Any?): R
複製代碼
不過若是誰寫的函數,直接傳參20多個還不封成對象或者builder,怕是腿都要被打斷.......
4、關於高階函數替換接口的討論
上面已經討論了,當接口只有一個方法時,確實能夠用高階函數代替,省略一個接口。
可是當接口有多個方法時,顯然不能直接替換。雖然也能夠把幾個函數包裝在一塊兒使用,可是仍是感受畫蛇添足。
多人並行開發的時候,好比一我的負責寫一個負責ui,一個負責使用ui處理業務邏輯。先把接口定好,接口方法文檔寫好,一目瞭然。這一方面仍是接口好不少,當只有簡單的一個方法時,用高階函數要方便一些。