定義
高階函數是將函數用作參數或返回值的函數。在java中,函數是不可以作爲參數或者返回值的,所以要實現這樣的功能,會使用接口來中轉,比如:
public interface Wrapper {
int method(int param);
}
int a(Wrapper wrapper) {
return wrapper.method(1);
}
a(wrapper1);
a(wrapper2);
在Kotlin中,函數是可以作爲參數的
fun a(funParam: (Int) -> String): String {
return funParam(1)
}
可以作爲返回值
fun c(param: Int): (Int) -> Unit {
...
}
如果要把函數賦值給一個對象,要使用雙":",
val d = ::b
雙冒號的寫法叫做函數引用 Function Reference,將函數變成對象,「函數可以作爲參數」的本質,是函數在 Kotlin 裏可以作爲對象存在,因爲只有對象才能被作爲參數傳遞。在 Kotlin 裏,一個函數名的左邊加上雙冒號,它就不表示這個函數本身了,而表示一個對象,或者說一個指向對象的引用,這個對象不是函數本身,而是一個和這個函數具有相同功能的對象
b(1) // 調用函數
d(1) // 用對象d 後面加上括號來實現 b() 的等價操作,實際上會調用 d.invoke(1)
(::b)(1) // 用對象 :b 後面加上括號來實現 b() 的等價操作,實際上會調用 (::b).invoke(1)
使用:
fun num1AndNum2(num1: Int, num2: Int, method: (a: Int, b: Int) -> Int): Int {
return method(num1, num2)
}
fun plus(num1: Int, num2: Int): Int {
return num1 + num2
}
//三種不同的調用方法
var num1 = 10
var num2 = 20
var result1 = num1AndNum2(num1, num2, ::plus)
println(result1)
var result2 = num1AndNum2(num1, num2, fun(num1: Int, num2: Int): Int {
return num1 - num2
})
println(result2)
//如果 Lambda 是函數的最後一個參數,可以把 Lambda 寫在括號的外面,
var result3 = num1AndNum2(num1, num2) { a, b ->
a + b
}
println(result3)
關於lambda的幾點說明
如果 Lambda 是函數的最後一個參數,可以把 Lambda 寫在括號的外面
view.setOnClickListener() { v: View ->
switchToNextPage()
}
如果 Lambda 是函數唯一的參數,可以直接把括號去掉
view.setOnClickListener { v: View ->
switchToNextPage()
}
如果 Lambda 是單參數的,這個參數可以省略掉不寫
view.setOnClickListener {
switchToNextPage()
}
如果用到這個參數,然後沒寫,可以使用it,Kotlin 的 Lambda 對於省略的唯一參數有默認的名字:it
view.setOnClickListener {
switchToNextPage()
it.setVisibility(GONE)
}
Lambda 的返回值不是用 return 來返回,而是直接取最後一行代碼的值
val b: (Int) -> String = {
it.toString() // it 可以被推斷出是 Int 類型
}
如果寫了return,會把這個作爲它外層的函數的返回值來直接結束外層函數。return 從最近的使用 fun 關鍵字聲明的函數返回, lambda 表達式沒有使用 fun關鍵字,所以 lambda 中的 return 從最外層的函數返回
內聯函數
每調用一次 lambda 表達式,一個額外的類就會被創建。如果 lambda 捕捉了某個變量,那麼每次調用的時候都會創建一個新的對象 。這就會帶來額外的開銷。如果使用 inline 修飾符標記一個函數,在函數被使用的時候編譯器並不會生成函數調用的代碼,而是使用函數實現的真實代碼替換每一次的函數調用。這樣的函數就是內聯函數,用於定義重複調用的函數,減少開銷。
使用inline 關鍵字只能提高帶有 lambda 參數的函數的性能,其他的情況需要額外的度量和研究。應該注意代碼的長度。如果要內聯的函數很大,將它的字節碼拷貝到每一個調用點將會極大地增加字節碼的長度。
inline fun doCal(a: Int, b: Int, cal: (a: Int, b: Int) -> Int): Int {
return cal(a, b)
}
如果希望只內聯一部分傳給內聯函數的 lambda 表達式參數, 那麼可以用 noinline 修飾符標記不希
望內聯的函數參數:
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { …… }