在Kotlin中函數是第一公民, 不像Java同樣, 函數必定須要寫在某個類裏面, Kotlin中的函數也能夠直接寫在文件裏.html
Lambda表達式並非什麼新東西, Java 8就有了. 它的存在主要是爲了讓代碼更加簡潔.bash
高階函數呢, 基本概念也很簡單, 就是函數, 一樣也能夠像其餘類型的對象同樣, 做爲一個函數的參數和返回值.ide
lambda表達式和匿名函數都是函數字面值(function literals), 它們沒有提早聲明, 直接做爲表達式傳入.函數
lambda表達式的構成:ui
{ 參數列表 -> 函數體 }
複製代碼
其中參數類型是能夠省略的. 若是函數體的返回值不是Unit
, 那麼最後一個表達式會被當作返回值.spa
對Lambda表達式的應用很大程度上要感謝現代化的IDE, 好比一個簡單的給button設置listener的代碼,code
最開始, 做爲一個把Kotlin當Java 7用的人, 可能會寫成這樣:htm
button.setOnClickListener(object : View.OnClickListener{
override fun onClick(v: View?) {
}
})
複製代碼
這裏利用了object
關鍵字, 聲明瞭一個匿名類的對象.對象
可是IDE看到這個代碼就不那麼開心了, 出現了一條黃色的下劃線, Alt + Enter, "Convert to lambda", 再Enter確認, 就變成了這樣:接口
button.setOnClickListener { }
複製代碼
爲何能夠這樣幹呢, 這是由於View
的OnClickListener
接口是一種特殊類型的接口: 只有一個方法.
這種只擁有一個方法的接口被稱做是函數式接口(functional interface), 也被叫作單抽象方法類型, 即SAM
, (single abstract method).
PS: 一個錯誤的示範: 若是你不幸一開始把代碼寫成了這樣:
button.setOnClickListener{object : View.OnClickListener {
override fun onClick(v: View?) {
}
}}
複製代碼
注意這裏與上面例子的區別僅僅是把小括號()變成了大括號{}. 程序沒有報錯, 但按鈕點擊的時候應該是執行不到你想要的代碼了.
IDE仍然會提示你簡化, 簡化後變成了這樣:
button.setOnClickListener{ View.OnClickListener { } }
複製代碼
也就是說每次按鈕點擊, 你作的事情都是建立了一個匿名對象.
爲何會犯這種錯誤, 而IDE又不提示呢, 這是由於lambda的慣例容許這樣的寫法, 這麼寫雖然邏輯上有問題, 可是語法上是沒有錯誤的. 下面請看都有什麼慣例呢.
it
.return
, 若是不顯式返回, 默認返回最後一個表達式的值._
表示.lambda表達式並不能顯式指定返回類型, 大多數狀況下可能用不上這一點, 由於每每返回類型都是能夠被自動推斷出來的.
可是若是你真的須要顯式指定, 你能夠用匿名函數.
fun(x: Int, y: Int): Int = x + y
複製代碼
匿名函數看起來和普通的函數聲明很像, 只是它沒有名字.
和普通函數不一樣的是, 若是參數類型能夠從上下文推斷出來, 那麼能夠省略不寫參數類型:
ints.filter(fun(item) = item > 0)
複製代碼
匿名函數的返回類型推斷和正常函數同樣.
匿名函數與Lambda的區別:
高階函數(Higher-Order Functions): 把函數做爲參數或返回值的函數.
函數類型(Function types): 函數根據參數和返回值, 能夠歸類到一個函數類型.
函數類型的基本寫法: 括號中的參數列表和一個返回值. 好比(A, B) -> C
就是一個函數類型, 這種類型的函數接受A和B兩種類型的參數, 返回一個類型C的返回值.
() -> A
.Unit
時不能省略.A.(B) -> C
. 表示這種函數是在A類型上調用的.suspend
關鍵字表示一種特殊的函數類型, 因此若是有, suspend
修飾符也要出現. 好比suspend () -> Unit
.(x: Int, y: Int) -> Point
.幾點比較特殊的:
?
. 好比((Int, Int) -> Int)?
.(Int) -> ((Int) -> Unit)
.(Int) -> (Int) -> Unit
和(Int) -> ((Int) -> Unit)
是一個意思.能夠給函數類型一個類型別名, 好比:
typealias ClickHandler = (Button, ClickEvent) -> Unit
複製代碼
實例化函數類型有好幾種方法:
::
的用法參見Callable Reference.函數類型聲明瞭, 也實例化了, 怎麼調用呢? 能夠用invoke
操做符, 也能夠直接用名稱調用.
若是有接受者類型(receiver), 那麼接受者對象須要做爲第一個參數. 另外一種方式也能夠將接受者對象放在點(.
)前面, 像擴展函數同樣調用.
val stringPlus: (String, String) -> String = String::plus
val intPlus: Int.(Int) -> Int = Int::plus
println(stringPlus.invoke("<-", "->"))
println(stringPlus("Hello, ", "world!"))
println(intPlus.invoke(1, 1))
println(intPlus(1, 2))
println(2.intPlus(3)) // extension-like call
複製代碼