Kotlin作用域函数深入理解

在Kotlin中,有许多的函数基于扩展函数实现,作用域函数也不例外。那么需要首先好好理解下扩展函数。

扩展函数
Kotlin 可以对一个类的属性和方法进行扩展,且不需要继承或使用 Decorator 模式。
扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。

为什么说是静态的呢,实际上从反编译的字节码文件可以发现,扩展函数在执行时会是一个静态方法,参数中会将接收者作为参数传递。

扩展函数代码

class User(var name:String)

/**扩展函数**/
fun User.print(){
    print("用户名 $name")
}

fun main(arg:Array<String>){
    var user = User("ExtFun")
    user.Print()
}

反编译后的代码

public final class ExtFunTestKt {
   public static final void print(@NotNull User $this$Print) {
      Intrinsics.checkNotNullParameter($this$Print, "<this>");
      String var1 = Intrinsics.stringPlus("用户名 ", $this$Print.getName());
      boolean var2 = false;
      System.out.print(var1);
   }

   public static final void main(@NotNull String[] arg) {
      Intrinsics.checkNotNullParameter(arg, "arg");
      User user = new User("ExtFun");
      Print(user);
   }
}

扩展函数特点:

扩展函数是静态解析的,并不是接收者类型的虚拟成员
在调用扩展函数时,具体被调用的的是哪一个函数,由调用函数的的对象表达式来决定的,而不是动态的类型决定的。

若扩展函数和成员函数一致,则使用该函数时,会优先使用成员函数
空对象也可以进行函数扩展
对伴生对象的扩展相当于增加静态方法,可通过类名直接调用
通常扩展函数或属性定义在顶级包下
在一个类内部你可以为另一个类声明扩展
属性也可以进行扩展,但是不能直接赋值,只能借助get()方法

作用域函数
本部分讲解作用域函数概念、区别

概念
作用域函数包含在标准库中,它们的唯一目的是在对象的上下文中执行代码块。对这些函数执行调用并提供一个lambda表达式,形成一个临时作用域,在此作用域中,可以访问该对象而无需其名称。这些函数称为作用域函数。 共有以下五种:let、run、with、apply 以及 also。

作用域函数的典型用法:

Person("Alice", 20, "Amsterdam").let {
        println(it)
        it.moveTo("London")
        it.incrementAge()
        println(it)
    }

//不使用作用域函数的情况下
 val alice = Person("Alice", 20, "Amsterdam")
    println(alice)
    alice.moveTo("London")
    alice.incrementAge()
    println(alice)

作用域函数相似性导致你出现选择性困难,下面总结下各个作用域函数的区别:
为了帮助你选择合适的作用域函数,我们提供了它们之间的主要区别表。

函数    对象引用    返回值    是否扩展函数
let    this    Lambda 表达式结果    是
run    this    Lambda 表达式结果    是
run    -    Lambda 表达式结果    不是:调用无需上下文对象
with    this    Lambda 表达式结果    不是:把上下文对象当做参数
apply    this    上下文对象    是
also    it    上下文对象    是
以下是根据预期目的选择作用域函数的简短指南:

对一个非空(non-null)对象执行 lambda 表达式:let
将表达式作为变量引入为局部作用域中:let
对象配置:apply
对象配置并且计算结果:run
在需要表达式的地方运行语句:非扩展的 run
附加效果:also
一个对象的一组函数调用:with
区别
作用域函数本质上都非常相似,区别主要有2点:

引用上下文对象的方式。
返回值。
区别之上下文对象
上下文对象是this 还是 it

run、with 以及 apply 通过关键字 this 引用上下文对象。
let 及 also 将上下文对象作为 lambda 表达式参数。

区别之返回值
apply 及 also 返回上下文对象。
let、run 及 with 返回 lambda 表达式结果.
takeIf 与 takeUnless
除了作用域函数外,标准库还包含函数 takeIf 及 takeUnless。这俩函数让你可以将对象状态检查嵌入到调用链中。

原理及实质
作用域函数使用了Kotlin的高阶函数及扩展函数。具体实现在标准库的
kotlin/utl/Standard.kt

//run是扩展函数
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}
//可以理解为一个泛型函数
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}
//扩展函数,返回是接收者本身
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}
// let是扩展函数,返回值是lambda表达式执行结果
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

猜你喜欢

转载自blog.csdn.net/qq_24252589/article/details/129967198
今日推荐