2019年6月14日 星期五

Kotlin 的 run, apply, let, also, with

參考文章: https://kotlinlang.org/docs/reference/scope-functions.html
要分辨 run, apply, let, also 等擴展函式的用法, 首先要明瞭 λ(lambda) 的區塊表達式:
      { parameters  ->  retval }
或是參考之前的文章:
      http://amitmason.blogspot.com/2018/08/kotlin-function.html         
其中箭頭前面是: 分別用逗號分開的輸入參數,箭頭之後的最後一行是函式的傳回值(簡稱函數),當數入參數只有一個時就可以使用 it 代表唯一參數, 並且可以省略箭頭符號與參數的宣告,就只剩下:
           {   ... ; retval }
物件產生後, 內建的 run { } 及 let  {  } 就是屬於這種型態的 λ, 兩者差別是 let 會將物件本身當唯一輸入參數也就是 it, 但 run 完全無輸入參數, 因此不能在 run 區塊裏面使用 it, 兩者相同的是: 將最後一行結果當作函數值傳回. 至於 also 同樣會將物件本身當唯一參數 it, 但不管最後一行的結果如何, also 的函數傳回值還是物件本身, apply 與 also 類似 , 函數傳回值也是物件本身,無輸入參數 it,  這些 run, let, apply, also 函式共通點是: 所有在區域(scope)內的從屬函式(member function)不需要引用物件句號, kotlin 自動會引用物件, 因此可以減少不少程式代碼, 下表就是根據 λ 的輸入與輸出列出函式差異, X = no,  V = Yes
                            it=物件輸入     函數輸出=物件本身
           run           X                       X                                   .run { member( );  retval   }
           let            V                       X                                   .let   { member( );  it.  ; ... ;  retval  }
           apply       X                       V                                  .apply{  member( );       }.apply{ ... }
           also          V                       V                                  .also { member( );  it. ; ...  }.also{ ...   }
由於 apply 與 also 函數傳回值就是物件本身, 因此可以不斷的串下去, 至於 run 與 let 傳回值是函式最後一行來指定, 因此適用於轉換不同物件時(transform)使用
說到 run 與 let 實際上的應用,以從屬函式(member function)的角度來看, let 有了 it 的加持後,就可從物件本身為起點全面展開, 但 run 只能從所屬函式為起點來展開:
val ret1 = objectTo.run { member1( ) ; member2( ); ... ;  ret_val }
val ret2 = objectTo.let   { 
           val some_ret = it.let { member1( );  member2( ); ... ; retval } 
           ...  Transform(it)
           ret_val
    } 
同樣觀點也適用於分辨 apply 與 also (帶有 it 物件本身)運用上的差異. 他們與 run 和 let 差異只在傳回物件的不同,  run 與 let 可以自訂傳回值, apply 與 aslo 傳回物已經內定是物件本身, 因此程式碼裏面沒有傳回值:
   objectTo.apply { member1( ) ; member2( ); ... }
   objectTo.also {
           it.also { member1( );  member2( ); ... }
           ...  Transform(it)           
    }
而且 run, let, aslo, apply 可以互相穿插與搭配, 從單純需求(輸入參數 it , 輸出本身物件 this)的角度來看:
1. 不用再處理物件本身(it), 單純用 run 或 apply 就夠了, 之後若要傳回計算值(函數), 就用 run,  否則用 apply
2. 不用轉換物件, 最常用到的是 apply 或 also, 若又要處理物件本身(it)時就用 also
3. 若還需要處理物件本身並轉換成不同物件就只能用帶輸入參數(it)的 let 
記憶口訣: let it be and run return, also it apply this. 意思是區分 let, run 和 also, apply 為兩組擴展函數, 只有 let run 可以自訂 return, 而 let 與 also 可以接 it, also apply 最後傳自我. 最後一個 with 其實跟 run 差不多, 差異在語法的使用, with 是 kotlin 的語法, 但 run 是擴展函數 λ( ), 操作  with(obj) { ... } 的結果就如同  obj.run { ... }  同樣都會把區塊最後一行的結果當作函數

沒有留言: