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))