Kotlin StandardKt 标准库源码走一波

距离上篇Kotlin文章,应该有差不多半年时间.没别的原因,因为,懒,而且,想产出一篇稍微质量好的博客好难. 最近在研究Python,所以最近也可能会更新一些Python的学习笔记.

StandardKt.kotlin源码位于kotlin-stdlib-common/kotlin包下; Kotlin的扩展函数有多爽,我应该不用再描述了吧~~; 如果对kotlin扩展函数概念不清的可以找找我前面的文章;

Kotlin提供了一个标准函数库,开发过程中使用十分频繁,也十分方便,只能说我已经彻底无法适应java开发了= =.

下面我们来探究一下这个标准库的源码.标准库函数容易混淆,但源码比较简单,不要慌.

标准库的函数基本都是内联函数 inline修饰.不知道啥是内联函数的,翻翻我前面的文章吧.

1.run函数

@kotlin.internal.InlineOnly
public inline fun  run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}
@kotlin.internal.InlineOnly
public inline fun  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.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  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.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.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.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.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给我解放的生产力.

你可能感兴趣的:(移动开发,java,python)