最近在 portal.kotlin-academy.com/#/ 上看到很多关于 Kotlin 的有趣的题目。个人觉得很适合 Kotlin 爱好者,感兴趣的小伙伴可自行查阅。
【有趣的 Kotlin 】系列记录自己对每一题的理解。
0x09:Extensions are resolved statically
open class C
class D: C()
fun C.foo() = "c"
fun D.foo() = "d"
fun printFoo(c: C) {
println(c.foo())
}
fun main(args: Array<String>) {
printFoo(D())
}
以上代码,运行结果是什么?可选项:
- Doesn't Compile
- Rutime error
- "c"
- "d"
思考一下,记录下你心中的答案。
分析
基本情况
-
类
C是 类D的父类,且各自有一个名为foo的扩展函数。类C的扩展函数返回字符串 “c” , 类D的扩展函数返回字符串 "d" ; -
定义了一个顶层函数
printFoo(),参数类型为C; -
main函数内调用printFoo函数,且传入参数为D的实例。
惯性思维
main() 函数中 printFoo() 传入参数为 D 对象,自然是调用 D 的扩展函数 D.foo() ,所以本题答案为——选项4:"d"。
这种分析对吗?
如果 Kotlin 扩展函数的本质是成员函数,这个分析是没有问题的,但是扩展函数的本质是成员函数吗?
Kotlin 扩展函数本质是什么?
遇事不明就举例
定义一个扩展函数,receiver 为 String。
fun String.suffix(suffix: String) = this + suffix
遇事不明反编译
- 字节码
- Decompile 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.