iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 13
0
Mobile Development

Android Kotlin開發 -小嫩雞的30篇精選筆記系列 第 13

Kotlin : higher order function高階函式、lambda表達式、inline function內聯函式

  • 分享至 

  • xImage
  •  

簡介

今天要介紹一些kotlin的特殊function用法,高階函式與lambda尤其常用,可以多留意。

高階函式-higher order function

將A函式作為參數傳入B函式,此B函式就是一個高階函式。
簡單舉例,我們現在寫了一個高階函式giveMoney,來決定誰要獲得多少錢。
因為pickAName這個方法符合findName方法的條件(傳入一個Int,回傳一個String),所以就可以傳入giveMoney。

fun main(args: Array<String>) {
    val str = giveMoney(3,2000,::pickAName)
    println(str)
}



fun giveMoney(num:Int,money:Int,findName:(number:Int)->String):String{
    return "${findName(num)} has ${money} dollars"

}


fun pickAName(num:Int):String{
    return when(num){
        1-> "Kai"
        2-> "Alvin"
        3-> "joey"
        else-> "WangWang"
    }
}

印出結果: joey has 2000 dollars


Lambda表達式

而kotlin提供了高階函式更簡便的寫法,也就是lambda表達式。以上述例子,我們可以按以下方式呼叫giveMoney函式:

fun main(args: Array<String>) {

    val str = giveMoney(3,2000,{num->
        when(num){
        1-> "Kai"
        2-> "Alvin"
        3-> "joey"
        else-> "WangWang"
        }
    })
    println(str)
}



fun giveMoney(num:Int,money:Int,findName:(number:Int)->String):String{
    return "${findName(num)} has ${money} dollars"

}

呼叫giveMoney函式時,將原本的pickAName函式做為一個參數直接傳入。


此時,如果lambda運算式是函式的最後一個參數,我們可以將大括號直接搬到外面來。

fun main(args: Array<String>) {

    val str = giveMoney(3,2000){num->
        when(num){
        1-> "Kai"
        2-> "Alvin"
        3-> "joey"
        else-> "WangWang"
        }
    }
    println(str)
}

而若Lambda運算式中只有一個參數,可以用it代替。

fun main(args: Array<String>) {
    val str = giveMoney(3,2000){
        when(it){
        1-> "Kai"
        2-> "Alvin"
        3-> "joey"
        else-> "WangWang"
        }
    }
    println(str)
}

舉一個常見的例子,我們寫專案都會需要預防快速連續點擊的click事件,但我們不可能在每個setOnClickListener裡面都先判斷一次是否連點才開始做事。這時候高階函式與lambda就派上用場了。

fun View.setMyClickListener(act: () -> Unit){
    setOnClickListener {
        if(!isFastDoubleClick()){
            act()
        }
    }
}

之後呼叫setMyClickListener只要這樣寫就好了:

button.setMyClickListener{
    //do something
}

內聯函式-inline function

使用高階函式會有一些缺點,例如在執行時期造成記憶體的消耗。每個傳遞的lambda函式都是一個物件,且還要在函式內部存取外層變數,這些都會消耗記憶體資源。

我們可以加入inline關鍵字來宣告一個內聯函式。其作用是能夠將函式的程式碼拷貝到呼叫的地方來展開。透過將Lambda運算式內聯在使用處,可以減少上述資源的消耗。為什麼呢?

  1. 函式的呼叫和返回是很消耗資源的(暫存器儲存和恢復),透過inline將函式內容直接拷貝至使用處展開,就可以省去呼叫函式帶來的開銷。
  2. 使用 inline 能避免函式的 lambda 形參額外建立 Function 物件,尤其是在有迴圈的情況下。
  3. 對於簡短的函式或是呼叫次數比較多的情況,適合使用inline。

舉個例子,我們現在使用了高階函式與lambda。

fun main(args: Array<String>) {
    printNames {
        println("kai")
    }
}

fun printNames(action:()->Unit){
    println("WangWang")
    action()

}

反編譯後會是這樣子,系統會建立一個function0物件來代表我們傳遞的那個action函式。它是0結尾是因為它本身沒有參數。系統仍然對外呼叫了兩個函式(Function0跟printNames),也就是說lambda只是語法糖而已,讓你寫起來方便,但是底層運作的本質上仍是不變的。

public final class MainThreadKt {
   public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      printNames((Function0)null.INSTANCE);
   }

   public static final void printNames(@NotNull Function0 action) {
      Intrinsics.checkParameterIsNotNull(action, "action");
      String var1 = "WangWang";
      boolean var2 = false;
      System.out.println(var1);
      action.invoke();
   }
}

加入inline

fun main(args: Array<String>) {
    printNames {
        println("kai")
    }
}



inline fun printNames(action:()->Unit){
    println("WangWang")
    action()

}

反編譯就會長這樣,會發現println("WangWang")跟println("kai")都被直接拷貝至main方法中,減少了呼叫函式產生的額外資源消耗。也避免了函式的 lambda 形參額外建立 Function 物件

public final class MainThreadKt {
   public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      int $i$f$printNames = false;
      String var2 = "WangWang";
      boolean var3 = false;
      System.out.println(var2);
      int var4 = false;
      String var5 = "kai";
      boolean var6 = false;
      System.out.println(var5);
   }

   public static final void printNames(@NotNull Function0 action) {
      int $i$f$printNames = 0;
      Intrinsics.checkParameterIsNotNull(action, "action");
      String var2 = "WangWang";
      boolean var3 = false;
      System.out.println(var2);
      action.invoke();
   }
}

參考資料

初探Lambda表達式


上一篇
Kotlin function : 可變參數與泛型參數
下一篇
Kotlin : Any類別、資料類別、具名引數、多載函式
系列文
Android Kotlin開發 -小嫩雞的30篇精選筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言