三、函數和lambda表達式
1. 函數聲明
fun double(x: Int): Int {
}
函數參數是用 Pascal 符號定義的 name:type。參數之間用逗號隔開,每個參數必須指明類型。函數參數可以有默認參數。這樣相比其他語言可以減少重載。
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size() ) {
...
}
2. 命名參數
在調用函數時可以參數可以命名。這對於有很多參數或只有一個的函數來說很方便。
fun reformat(str: String, normalizeCase: Boolean = true,upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ') {
...
}
reformat(str,
normalizeCase = true,
uppercaseFirstLetter = true,
divideByCamelHumps = false,
wordSeparator = '_'
)
3. 變長參數:
fun asList<T>(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts)
result.add(t)
return result
}
傳遞一個 array 的內容給函數,我們就可以使用 * 前綴操作符:
val a = array(1, 2, 3)
val list = asList(-1, 0, *a, 4)
Kotlin 中可以在文件個根級聲明函數,這就意味者你不用創建一個類來持有函數。除了頂級函數,Kotlin 函數可以聲明為局部的,作為成員函數或擴展函數。
Kotlin 支持局部函數,比如在另一個函數使用另一函數。局部函數可以訪問外部函數的局部變量(比如閉包)。局部函數甚至可以返回到外部函數
4. 高階函數
高階函數就是可以接受函數作為參數並返回一個函數的函數。比如 lock() 就是一個很好的例子,它接收一個 lock 對象和一個函數,運行函數並釋放 lock;
fun lock<T>(lock: Lock, body: () -> T ) : T {
lock.lock()
try {
return body()
}
finally {
lock.unlock()
}
}
其實最方便的辦法是傳遞一個字面函數(通常是 lambda 表達式):
val result = lock(lock, { sharedResource.operation() })
在 kotlin 中有一個約定,如果最后一個參數是函數,可以省略括號:
lock (lock) {
sharedResource.operation()
}
5. 字面函數
- 字面函數被包在大括號里
- 參數在 -> 前面聲明(參數類型可以省略)
- 函數體在 -> 之后
字面函數或函數表達式就是一個 "匿名函數",也就是沒有聲明的函數,但立即作為表達式傳遞下去。
6. 函數類型
如:(T, T) -> Boolean
注意如果一個函數接受另一個函數做為最后一個參數,該函數文本參數可以在括號內的參數列表外的傳遞。
7. 函數文本語法
函數文本的完全寫法是下面這樣的:
val sum = {x: Int,y: Int -> x + y}
函數文本總是在大括號里包裹着,在完全語法中參數聲明是在括號內,類型注解是可選的,函數體是在 -> 之后,像下面這樣:
val sum: (Int, Int) -> Int = {x, y -> x+y }
8. 函數表達式
fun(x: Int, y: Int ): Int = x + y
9. 內聯函數
在高階函數前增加inline注解可以指定函數內聯。inline 標記即影響函數本身也影響傳遞進來的 lambda 函數:所有的這些都將被關聯到調用點。
內聯可能會引起生成代碼增長,所以可以使用noinline來指定某些函數不進行內聯。
注意有些內聯函數可以調用傳遞進來的 lambda 函數,但不是在函數體,而是在另一個執行的上下文中,比如局部對象或者一個嵌套函數。在這樣的情形中,非局部的控制流也不允許在lambda 函數中。為了表明,lambda 參數需要有 InlineOptions.ONLY_LOCAL_RETURN 注解
參考: