本人也是在初學Kotlin,如有錯誤,請幫忙指出,持續更新
Android:Kotlin詳細入門學習指南-高階函數-基礎語法(七)
建議先看看前面的文章
Android:Kotlin詳細入門學習指南-基礎語法(一)
Android:Kotlin詳細入門學習指南-基本類型-基礎語法(二)
Android:Kotlin詳細入門學習指南-包-控制流-返回與跳轉-基礎語法(三)
Android:Kotlin詳細入門學習指南-類和對象-基礎語法(四)
Android:Kotlin詳細入門學習指南-類和對象(下)-基礎語法(五)
Android:Kotlin詳細入門學習指南-函數-基礎語法(六)
這篇文章分享的內容比較多,建議先關注收藏,再查看,以免迷路
高階函數
高階函數就是可以接受函數作爲參數並返回一個函數的函數
字面函數和函數表達式
字面函數或函數表達式就是一個 "匿名函數",也就是沒有聲明的函數,但立即作爲 表達式傳遞下去。
max(strings, {a, b -> a.length < b.length })
max 函數就是一個高階函數,它接受函數作爲第二個參數。第二個參數是一個表達 式所以本生就是一個函數,即字面函數。作爲一個函數,相當於:
fun compare(a: String, b: String) : Boolean = a.length < b.lengt h
函數類型
一個函數要接受另一個函數作爲參數,我們得給它指定一個類型。
fun max<T>(collection: Collection<out T>, less: (T, T) -> Boolean ): T? {
var max: T? = null
for (it in collection)
if (max == null || less(max!!, it))
max = it
return max
}
參數 less 是 (T, T) -> Boolean 類型,也就是接受倆個 T 類型參數返回一 個 Boolean :如果第一個參數小於第二個則返回真。 在函數體第四行, less 是用作函數
val compare: (x: T,y: T) -> Int = ...
函數文本語法
val sum = {x: Int,y: Int -> x + y}
函數文本總是在大括號裏包裹着,在完全語法中參數聲明是在括號內,類型註解是 可選的,函數體是在 -> 之後,像下面這樣:
val sum: (Int, Int) -> Int = {x, y -> x+y }
函數文本有時只有一個參數。如果 kotlin 可以從它本生計算出簽名,那麼可以省略 這個唯一的參數,並會通過 it 隱式的聲明它:
```kotlin
ints.filter {it > 0}//這是 (it: Int) -> Boolean 的字面意思
注意如果一個函數接受另一個函數做爲最後一個參數,該函數文本參數可以在括號 內的參數列表外的傳遞。
函數表達式
上面沒有講到可以指定返回值的函數。在大多數情形中,這是不必要的,因爲返回 值是可以自動推斷的。然而,如果你需要自己指定,可以用函數表達式來做:
fun(x: Int, y: Int ): Int = x + y
閉包
一個字面函數或者表達式函數可以訪問閉包,即訪問自身範圍外的聲明的變量。不 像 java 那樣在閉包中的變量可以被捕獲修改:
var sum = 0 ins filter {it > 0} forEach {
sum += it
}
print(sum)
內聯函數
inline 標記即影響函數本身也影響傳遞進來的 lambda 函數:所有的這些都將被 關聯到調用點。 內聯可能會引起生成代碼增長,但我們可以合理的解決它(不要內聯太大的函數)
inline fun lock<T>(lock: Lock,body: ()-> T): T { //... }
爲了你想要一些 lambda 表達式傳遞給內聯函數時是內聯的,你可以給你的一些函 數參數標記 @noinline 註解:
inline fun foo(inlined: () -> Uint, @noinline notInlined: () -> Unit) { //... }
內聯的 lambda 只能在內聯函數中調用,或者作爲內聯參數,但 @noinline 標記 的可以通過任何我們喜歡的方式操控:存儲在字段,( passed around etc)
返回到非局部
在 kotlin 中,我們可以不加條件的使用 return 去退出一個命名函數或表達式函 數。這意味這退出一個 lambda 函數,我們不得不使用標籤,而且空白的 return 在 lambda 函數中是禁止的,因爲 lambda 函數不可以造一個閉合函數返回:
fun foo() {
ordinaryFunction {
return // 錯誤 不可以在這返回
}
}
但如果 lambda 函數是內聯傳遞的,則返回也是可以內聯的,因此允許下面這樣:
fun foo() {
inlineFunction {
return //
]
}
注意有些內聯函數可以調用傳遞進來的 lambda 函數,但不是在函數體,而是在另 一個執行的上下文中,比如局部對象或者一個嵌套函數。在這樣的情形中,非局部 的控制流也不允許在lambda 函數中。
內聯 lambda 不允許用 break 或 continue ,但在以後的版本可能會支持。
實例化參數類型
有時候我們需要訪問傳遞過來的類型作爲參數:
fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? {
var p = parent
while (p != null && !clazz.isInstance(p)) {
p = p?.parent
}
@suppress("UNCHECKED_CAST")
return p as T
}
現在,我們創立了一顆樹,並用反射檢查它是否是某個特定類型。一切看起來很 好,但調用點就很繁瑣了:
myTree.findParentOfType(javaClass<MyTreeNodeType>() )
我們想要的僅僅是給這個函數傳遞一個類型,即像下面這樣:
myTree.findParentOfType<MyTreeNodeType>()
爲了達到這個目的,內聯函數支持具體化的類型參數,因此我們可以寫成這樣:
inline fun <reified T> TreeNode.findParentOfType(): T? {
var p = parent
while (p != null && p !is T) {
p = p?.parent
}
return p as T
}
用 refied 修飾符檢查類型參數,既然它可以在函數內部訪問了,也就基本上接 近普通函數了。因爲函數是內聯的,所以不許要反射,像 !is `as`這樣的操作 都可以使用。同時,我們也可以像上面那樣調用它了
myTree.findParentOfType<MyTreeNodeType>()
儘管在很多情況下會使用反射,我們仍然可以使用實例化的類型參數 javaClass() 來訪問它:
inline fun methodsOf<reified T>() = javaClass<T>().getMethods()
fun main(s: Array<String>) {
println(methodsOf<String>().joinToString('\n'))
}
普通的函數(沒有標記爲內聯的)不能有實例化參數。