我們需要掌握的Kotlin之各種函數

Kotlin的函數可以定義在文件頂部,也就是說不需要創建一個類來持有一個函數。另外,Kotlin函數還包括:本地函數、成員函數、擴展函數、內聯函數、高階函數、泛型函數、遞歸函數。

一、本地函數

本地函數是函數內部包含另一個函數,也就是函數嵌套,示例代碼如下:

fun dfs(graph: Graph) {
    fun dfs(current: Vertex, visited: Set<Vertex>) {
        if (!visited.add(current)) return
        for (v in current.neighbors)
            dfs(v, visited)
    }

    dfs(graph.vertices[0], HashSet())
}

本地函數可以訪問外部函數的本地變量,因此上述例子可以這樣寫:

fun dfs(graph: Graph) {
    val visited = HashSet<Vertex>()
    fun dfs(current: Vertex) {
        if (!visited.add(current)) return
        for (v in current.neighbors)
            dfs(v)
    }

    dfs(graph.vertices[0])
}

二、成員函數

成員函數是定義在類內部,與Java一樣,示例代碼如下:

class Sample() {
    fun hello() { print("Hello World") }
}

成員函數可以使用點引用來調用:

Sample().hello()

三、泛型函數

泛型函數是指函數中,參數或返回值類型定義成泛型,使用T表示:

fun <T> singletonList(item: T): List<T> { 
  print("This is generic function")
}

四、內聯函數

內存分配(包含函數對象和類)和虛函數調用,會帶來運行時開銷。但是,這些開銷可以通過內聯的lambda表達式來消除。對於編譯器來說,我們需要使用inline修飾符來標識:

inline fun <T> lock(lock: Lock, body: () -> T): T {
  print("This is an inline function")
}

內聯修飾符會影響函數本身和lambda傳遞,這些都要內聯到調用端。內聯可能會引起代碼增長。但是,如果我們使用恰當(避免在龐大函數中使用),它可以提升性能。

1、noinline

如果,我們只想某個lambda表達式內聯,而不是整個函數內聯。我們可以使用inlined和ontInlined修飾符來標識:

inline fun hello(inlined: () -> Unit, noinline notInlined: () -> Unit) {
  print("This is inlined lambda")
}

2、crossinline

有些函數傳遞lambda參數不是通過函數體本身,而是從另一個執行體,比如本地對象或者嵌套函數。這種情況下,non-local控制流也不允許出現在lambda表達式中。這時,需要使用crossline修飾符來標識:

inline fun f(crossinline body: () -> Unit) {
    val f = object: Runnable {
        override fun run() = body()
    }
}

3、inline properties

內聯修飾符可以用於屬性的訪問,我們可以這樣註解單個屬性:

val foo: Foo
    inline get() = Foo()

var bar: Bar
    get() = ...
    inline set(v) { ... }

五、擴展函數

Kotlin提供類的擴展功能,不必用繼承類的方法或者使用裝飾者的設計模式。可以通過extensions來特殊聲明,Kotlin支持擴展函數和擴展屬性。聲明一個擴展函數,我們需要在名稱前面加一個接收類型。如下代碼是往MutableList<Int>添加swap功能:

fun MutableList<Int>.swap(index1: Int, index2: Int) {
    val tmp = this[index1] // 'this' corresponds to the list
    this[index1] = this[index2]
    this[index2] = tmp
}

擴展函數中的this關鍵字對應接收者類型。現在,我們可以調用MutableList<Int>中的swap方法:

val l = mutableListOf(1, 2, 3)
l.swap(0, 2)

六、高階函數

高階函數是把函數作爲參數或者返回值的函數。示例代碼如下:

fun <T, R> Collection<T>.fold(
    initial: R, 
    combine: (acc: R, nextElement: T) -> R
): R {
    var accumulator: R = initial
    for (element: T in this) {
        accumulator = combine(accumulator, element)
    }
    return accumulator
}

七、遞歸函數 

Kotlin支持特色功能性編程——尾遞歸。可以運行算法使用loop循環來代替遞歸函數,這樣可以避免棧溢出。當使用tailrec修飾符時,遇到特定格式,編譯器會優化遞歸,改成快速高效的基於loop循環的版本。示例代碼如下:

tailrec fun findFixPoint(x: Double = 1.0): Double
        = if (Math.abs(x - Math.cos(x)) < eps) x else findFixPoint(Math.cos(x))

 

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