两个this 一起用?Kotlin 的成员扩展函数和 implicit receiver
发布网友
发布时间:2024-10-24 12:56
我来回答
共1个回答
热心网友
时间:2024-10-27 06:40
大家好,我是扔物线朱凯。
今天咱说说 Kotlin 的 implicit receiver。这是一个我们写 Kotlin 经常会用的东西,虽然你可能都没听过这个词,但你一定用过它。Kotlin 的很多高级功能,都利用到了这个概念——比如协程,协程是重度依赖它的,非常重。所以,弄明白它是个什么、怎么用、怎么去发挥它最大的价值,对我们的能力提升是非常有帮助的。
定义:其实隐式的接收器就是this。所谓的接收,其实指的就是接收调用,或者说接受调用。接受函数的调用啊,接受属性的访问啊。比如这个user.name:
左边的user 就是它的 receiver。谁的 receiver?对于 name 的访问的 receiver。
而隐式的 receiver,指的就是不用写也自动存在的 receiver。也就是如果我把这个user. 给删了,它依然能取到某个 User 对象的 name:
那么这个隐式地被应用的User 对象,就是对这个 name 的访问的 implicit receiver,隐式的 receiver。
嵌套的 implicit receiver 在 Kotlin 里是可以嵌套的,比如在 Java 里我们可以这么写:
我在这个内部类的里面,想访问内部类和外部类的成员都是可以的,是吧:
这个innerInt 是 InnerClass 里的,所以它等价于加上 this 的写法:
而下面的outerInt 属于外面的 OuterClass,但为了避免歧义,Java 不允许我们直接写 this:
而需要显式地加上OuterClass 的前缀:
而上面的innerInt 如果展开,前缀是 InnerClass:
也就是说,在内部类的里面,我是有内部类和外部类的双重this 的。对吧?
另外,对于它们同名的成员变量或者方法,如果我也省略掉this:
拿到的就是内部类的成员。如果想拿外部类的,就必须把this 写完整:
Kotlin 增加的 implicit receiver 嵌套:通过函数的 receiver 指定
然后,Kotlin 对于这种嵌套,又新增了一类场景——咱刚才看的是通过内部类来嵌套是吧?Kotlin 让我们还可以直接通过函数来嵌套新的this。比如你有一个在类型内部声明的扩展函数:
——这种函数叫 member extension function,成员扩展函数,其实就是字面意思:它既是成员函数又是扩展函数,对吧?
这种「成员扩展函数」有一个问题:一方面,因为它是Int 的扩展函数,所以你需要对 Int 类型的对象才能调用它;但同时,它也是 IntMultiplier 的成员函数,所以你还要求你对 IntMultiplier 对象调用它:
也就是说,这里需要的是个双重 receiver:既要这个直接的Int,又要那个外部的 IntMultiplier,缺一不可。——那我到底对谁调用?
Kotlin 提供的解法是,你专门创建一个函数,并给它设置一个函数类型的参数:
函数不用做什么特别的事,关键是执行一下它的那个函数类型的参数:
另外,你要给这个函数类型的参数,设置一个 receiver 的类型:
这么一指定,就把参数的函数体内部——注意,是这个block 的函数体,不是外部函数本身的函数体——在它内部强行安插了一个隐式的 receiver。换句话说,我在调用这个外部函数的时候,它的函数类型的参数的大括号里就有一个 IntMultiplier 类型的 this 了:
通过这种写法,我们就可以任意地往代码里插入我们指定的 implicit receiver,或者说指定的this,去应对「多个 this」的需求场景了。
协程里的应用
Kotlin 的官方代码,以及很多第三方库,都重度地依赖这个叫做 implicit receiver 的东西。虽然我们可以说「它不就是this 嘛」,但关键是,它给我们带来了很大的方便,怎么叫其实是次要的。随便举个例子,我们知道协程的启动是一定要用 CoroutineScope 才行的:
但是为什么在协程的内部再启动新的协程,就不用写CoroutineScope 了?
因为它有一个隐式的CoroutineScope 作为 this 被提供了:
总结
其他很多官方源码以及第三方库,都有类似的应用,而我们自己也可以在代码里用这样的写法去安插新的this 层级,或者说——安插 implicit receiver,隐式的 receiver。看起来好像很复杂,但当你明白它的这些本质逻辑,写起来就很简单了。