Kotlin知識總結:高階函數

定義

高階函數是將函數用作參數或返回值的函數。在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) { …… }

參考:Kotlin 的 Lambda 表達式

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