Android:Kotlin詳細入門學習指南-高階函數-基礎語法(七)

本人也是在初學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'))
}

普通的函數(沒有標記爲內聯的)不能有實例化參數。

這篇文章主要分享Kotlin中的高階函數.

本人也是在初學Kotlin,如有錯誤,請幫忙指出,持續更新

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章