在Kotlin中,有许多的函数基于扩展函数实现,作用域函数也不例外。那么需要首先好好理解下扩展函数。
扩展函数
Kotlin 可以对一个类的属性和方法进行扩展,且不需要继承或使用 Decorator 模式。
扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。
为什么说是静态的呢,实际上从反编译的字节码文件可以发现,扩展函数在执行时会是一个静态方法,参数中会将接收者作为参数传递。
扩展函数代码
class User(var name:String)
/**扩展函数**/
fun User.print(){
print("用户名 $name")
}
fun main(arg:Array){
var user = User("ExtFun")
user.Print()
}
反编译后的代码
public final class ExtFunTestKt {
public static final void print(@NotNull User $this$Print) {
Intrinsics.checkNotNullParameter($this$Print, "");
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
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
//可以理解为一个泛型函数
public inline fun
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
//扩展函数,返回是接收者本身
public inline fun
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
// let是扩展函数,返回值是lambda表达式执行结果
public inline fun
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}