Kotlin StandardKt 標準庫原始碼走一波

ZENLovely發表於2019-03-12

距離上篇Kotlin文章,應該有差不多半年時間.沒別的原因,因為,懶,而且,想產出一篇稍微質量好的部落格好難. 最近在研究Python,所以最近也可能會更新一些Python的學習筆記.

StandardKt.kotlin原始碼位於kotlin-stdlib-common/kotlin包下; Kotlin的擴充套件函式有多爽,我應該不用再描述了吧~~; 如果對kotlin擴充套件函式概念不清的可以找找我前面的文章;

Kotlin提供了一個標準函式庫,開發過程中使用十分頻繁,也十分方便,只能說我已經徹底無法適應java開發了= =.

下面我們來探究一下這個標準庫的原始碼.標準庫函式容易混淆,但原始碼比較簡單,不要慌.

標準庫的函式基本都是行內函數 inline修飾.不知道啥是行內函數的,翻翻我前面的文章吧.

1.run函式

@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}
複製程式碼

run函式在標準庫有兩個; 我們直接看第二個擴充套件函式.入參是一個block函式,型別是一個T的擴充套件函式T.()->R,無參,返回值是R型別 ,也可以傳入lambda表示式,記住,kotlin中萬物皆物件,所以函式也是個物件; 例子:

  /**
     * 取絕對值
     */
   fun Int.pow2():Double{
      return Math.pow(toDouble(),2.toDouble())
   }
   
    fun testFun(){

        val pow2 = "I am Zen".length.run({pow2()}) //1
        Log.d(TAG,"pow2:$pow2")

    }
    
    執行testFun(),輸出:pow2:64.0
    
   在Kotlin中,如果引數是最後一個引數,{}可以提到()之後,那麼上述1式子可以演變==>
   val pow2 = "I am Zen".length.run(){pow2()}
   Koltin中又規定,如果引數是唯一一個引數,那麼()也可以省略,1式子可以演變==>
   val pow2 = "I am Zen".length.run{pow2()}
   
  同樣的,如果直接傳入lamda表示式,也是ok的,1式可以這樣寫==>
  val pow2 = "I am Zen".length.run{Math.pow(toDouble(),2.toDouble())}
    
   
複製程式碼

2.let函式

@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}
複製程式碼

同學們,仔細觀察,let函式和run函式的區別 可以看到,T.let 入參傳入的block函式,其引數是T,返回值依舊是R,block在內部呼叫時傳入T.this;

 /**
     * 取絕對值
     */
   fun Int.pow2():Double{
      return Math.pow(this.toDouble(),2.toDouble())
   }
   
    fun testFun(){

        val pow2 = "I am Zen".length.let { Math.pow(it.toDouble(),2.toDouble())} //1
        Log.d(TAG,"pow2:$pow2")

    }
    
    執行testFun(),輸出:pow2:64.0
    
    仔細觀察這個例子,可以看到它和run函式的區別在於,run傳入的block函式內部直接持有的是T的引用;而let是外部傳入的T的引用,在lamda中用預設用it表示;
複製程式碼

3.with函式

@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}
複製程式碼

with函式和上述兩個又很像,= =,我們來仔細辨別一下,它又有什麼不同

fun testFun(){
        
        val pow2 =  with("I am Zen".length){
            Math.pow(toDouble(),2.toDouble())
        }
        Log.d(TAG,"pow2:$pow2")

    }
    
  執行testFun(),輸出:pow2:64.0
  with函式有兩個入參receiver:T,block函式,關於T的擴充套件函式,返回R
  
  
複製程式碼

4.also函式

public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}
複製程式碼

also函式和let函式的區別在於,also返回的自身,入參block函式,無返回值

val also = "I am Zen".length.also {
            Math.pow(it.toDouble(), 2.toDouble())
        }
        Log.d(TAG,"also:$also")
        
   
   
   輸出:also:8
   
   字串"I am Zen".length為8,對8執行also函式,內部持有外部傳入的T引用,用it表示,內部計算了it的二次冪,但是返回的還是T本身,也即是8;所以,不難理解吧;和also函式類似的是apply函式.
     
複製程式碼

5.apply

@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}
複製程式碼

apply函式十分常用,它可以輕鬆實現java的鏈式呼叫,我們不用再那麼麻煩寫build模式啦; 可以看出apply函式和also函式很相識,不同的是,對於lamda內部,apply函式中直接持有T的引用,this可以省略,所以可以直接呼叫關於T的所有api,而also持有的是外部傳入的T的引用,用it表示,所以需要用it來呼叫關於T的所有api

6.takeIf,takeUnless

@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
    contract {
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    }
    return if (predicate(this)) this else null
}

@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
    contract {
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    }
    return if (!predicate(this)) this else null
}
複製程式碼

takeIf和takeUnless只是斷言相反 所以只講一下takeIf 這也是個十分實用的標準函式,傳入的predicate斷言函式,返回值Boolean,對於takeIf而言,符合條件則返回T,否則返回null,話不多說,直接看例子:

 		val age=17

        val adultAge= age.takeIf {
            it >= 18
        }

        Log.d(TAG,"isAdult or null:$adultAge")
       
        輸出:isAdult or null:null
        
      predicate函式斷言條件就是>=18;17執行takeIf,斷言失敗,返回null;  
      
     
        
複製程式碼

7.repeat函式

@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
    contract { callsInPlace(action) }

    for (index in 0 until times) {
        action(index)
    }
}
複製程式碼

這個標準函式可以輕鬆實現迴圈任務;time是迴圈的次數,action函式指定具體迴圈的動作

repeat(5){

            Log.d(TAG,"print:$it")
        }      
  輸出:
  print:0
  print:1
  print:2
  print:3
  print:4


複製程式碼

結尾

對於java基礎者,Koltin是一門十分容易上手的語言,而且在使用了將近一年的時間後,我深深感到Kotlin給我解放的生產力.

相關文章