apply函数是如何做到支持接收者对象隐式调用的?
1、apply函数用法
apply函数可看作一个配置函数,你可以传入一个接收者,然后调用一系列函数来配置它以便使用,如果提供lambda给apply函数执行,它会返回配置好的接收者。
val file = File("xxxx").apply {
setReadable(true)
}
那么在上述示例中:
接收者对象:File(“xxxx”).apply中,File(“xxxx”)既为调用者也为接收者
隐式调用:file.setReadable(true)为显式,setReadable(true)为隐式
2、apply函数结构分析
apply函数源码
public inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
2.1、入参
block: T.() -> Unit
入参是一个匿名函数,该函数返回Unit,相当于java中的void,不返回参数
类似于这种写法
// 无参,无返回
fun doSomething(fix: () -> Unit) {
}
2.2、返回参数
(block: T.() -> Unit): T
函数声明里面:说明还是返回T类型,与调用者类型一致
函数体里面:这里的return this说明apply函数返回的还是调用者对象,在这个例子里面是File(“xxxx”)
3、T.()是啥
那么T.()是啥意思
回顾扩展函数、以及泛型扩展函数
// 扩展函数
fun String.addExt(amount: Int = 1) = this + "!".repeat(amount)
// 泛型的扩展函数
fun <T> T.easyPrint(): Unit = println(this)
fun main() {
"asdf".easyPrint()
}
这里T.()类似于T.easyPrint(),
结论:T.()也是一个泛型扩展函数
4、为什么用泛型扩展函数T.()
为什么要传入一个泛型扩展函数,而不是一个普通的函数?
case 如果用普通函数
删除泛型扩展函数为普通函数,即T.() -> Unit变为() -> Unit
编译报错,setReadable(true)没有调用者this了
得出结论:扩展函数,是为了有this可以使用
在下面示例中,将字符串转换为感叹号
// 扩展函数(字符转感叹号)
fun String.addExt() = "!".repeat(count())
// 泛型的扩展函数
fun <T> T.easyPrint(): Unit = println(this)
fun main() {
"abcefg".addExt().easyPrint()//!!!!!!
}
那么count()即this.count(),可省略
结论:扩展函数里面自带了接受者对象的this隐式调用
case 如果用确定类型
为什么是泛型的扩展函数
下面示例中,如果是确定类型File,那么别的类型如String就用不了
//public inline fun <T> T.apply(block: T.() -> Unit): T {
// block()
// return this
//}
public inline fun File.apply(block: File.() -> Unit): File {
block()
return this
}
fun main() {
val file = File("").apply {
setReadable(true)
}
}
分析。这里File.(),匿名函数内部this指向一个File对象,隐式调用就是这么来的
File.() -> Unit
同理T.(),则表示泛型类型的匿名函数,也就实现了隐式调用
T.() -> Unit
结论:泛型,是为了有更多类型可以使用
4、apply步骤分解
fun main() {
val file = File("").apply {
setReadable(true)
}
//1,定义扩展函数
fun File.ext(): Unit {
setReadable(true)
}
//2,给block变量赋值
val block = File::ext
//3,传入apply函数
File("xxxx").apply(block)
//或者
File("xxxx").apply { block }
}