阅读 169

有趣的 Kotlin 0x09:Extensions are resolved statically

最近在 portal.kotlin-academy.com/#/ 上看到很多关于 Kotlin 的有趣的题目。个人觉得很适合 Kotlin 爱好者,感兴趣的小伙伴可自行查阅。

【有趣的 Kotlin 】系列记录自己对每一题的理解。

0x09:Extensions are resolved statically

open class Cclass D: C()
​
fun C.foo() = "c"fun D.foo() = "d"fun printFoo(c: C) {
    println(c.foo())
}
​
fun main(args: Array<String>) {
    printFoo(D())
}
复制代码

以上代码,运行结果是什么?可选项:

  1. Doesn't Compile
  2. Rutime error
  3. "c"
  4. "d"

思考一下,记录下你心中的答案。

分析

基本情况

D 继承 C

  • C 是 类 D 的父类,且各自有一个名为 foo 的扩展函数。类 C 的扩展函数返回字符串 “c” , 类 D 的扩展函数返回字符串 "d"

  • 定义了一个顶层函数 printFoo() ,参数类型为 C

  • main 函数内调用 printFoo 函数,且传入参数为 D 的实例。

    如何选择 foo

惯性思维

main() 函数中 printFoo() 传入参数为 D 对象,自然是调用 D 的扩展函数 D.foo() ,所以本题答案为——选项4:"d"。

这种分析对吗?

如果 Kotlin 扩展函数的本质是成员函数,这个分析是没有问题的,但是扩展函数的本质是成员函数吗?

Kotlin 扩展函数本质是什么?

遇事不明就举例

定义一个扩展函数,receiver 为 String。

fun String.suffix(suffix: String) = this + suffix
复制代码

遇事不明反编译

  • 字节码

扩展函数字节码

  • Decompile Java 代码

Java 代码

  • Kotlin 扩展函数 在 Java 使用
public class Test {
​
    public static void main(String[] args) {
        // 两个参数且为类成员函数
        String result =
            Extensions_are_resolved_staticallyKt.suffix("Hello", " OpenCV or Android"); 
        System.out.println(result);
    }
}
复制代码

综上, Kotlin 扩展函数的本质是静态函数,且 receiver 是字节码中对应静态函数的第一个参数。所以 Java 中调用 Kotlin 定义的扩展函数时,需要传入 receiver作为第一个参数。

解题

回到题目

fun printFoo(c: C) {
    println(c.foo())
}
复制代码

由于参数类型为 C,所以代码中 c.foo() 对应的是 fun C.foo() = "c" 编译成的静态函数。虽然运行时传入的是 D(),但是依然会被强转成 C 然后执行 fun C.foo() = "c" 对应的静态方法。所以,题中,正确答案为 :

选项 3 :"c"

题目分析

延伸

我们给类 C 和 类 D 分别添加成员函数 foo()

package puzzlers
​
open class C {
    open fun foo() = "cc"       // 类 C 成员函数 foo
}
​
class D : C() {
    override fun foo() = "dd"   // 类 D 成员函数 foo
}
​
fun C.foo() = "c"
​
fun D.foo() = "d"
​
fun printFoo(c: C) {
    println(c.foo())
}
​
fun main(args: Array<String>) {
    printFoo(D())
}
复制代码

运行结果又该为何 ?友情提示 member always win.

文章分类
Android
文章标签