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了


android studio kotlin 自动补齐注释 kotlin also apply_泛型

得出结论:扩展函数,是为了有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 }
}