作者:feintkotlin
学习过Kotlin的人,应该都能够感受到Kotlin中扩展的便利。那么Kotlin的这个特性在底层是怎么实现的呢?还没使用过扩展的同学,可以看下下面这个简单的例子感受一下:
文件:Cat.kt
class Cat {
fun run()="Running"
}
fun Cat.miao() = "Miao"
文件:Main.kt
fun main(args: Array<String>) {
animalAction()
}
fun animalAction(){
val cat=Cat()
println(cat.miao())
println(cat.run())
}
将扩展函数作为包级函数
IDEA有一个功能,可以把Kotlin代码解析成字节码。像下面那张图所示
坦白说,字节码阅读起来,挺费劲的。为了阅读的方便,可以点击图片左上方的 Decompile 按钮,将字节码文件反编译成一个 java 文件。
这张图所示的是文章开头演示代码中Cat.kt文件中的代码所对应的java代码。可以看到在图中有一个CatKt的类,而在kotlin的文件中我们并没有定义这样一个类。当Kotlin文件中存在包级元素时,就会生成一个 文件名+Kt的类。我们定义那些包级的函数和属性都会变成这个类的静态成员。
像Cat.kt中定义的一个扩展函数:fun Cat.mian() ,属于一个包级的函数,因此在Java代码中它变成了CatKt的一个静态成员。并且接受一个Cat类型的参数:
public static final String miao(@NotNull Cat $receiver)
在这里说句题外话,上方的Java代码还验证了Kotlin的另一个特性:函数和类默认都是final的(不能被重写)
当我们在kotlin中使用 Cat().miao() 调用 miao() 函数时,实际上并不是调用Cat类中的成员函数。它对应着下方的Java代码:
CatKt.miao(new Cat())
将扩展函数放置在被扩展类中
如果,我们将扩展函数 miao 定义在 Cat类的内部会怎么样呢?
它变成了 Cat 类的一个成员。你没办法在Cat类之外的地方调用到miao这个函数了。当你使用 Cat().miao() 时,在Cat内部时,其会被解析成this.miao(this)。感觉可以通过这种方式来定义一个私有成员(只能在类内部调用)。
将扩展函数放置到另一个类中
还有一种情况,我们定义一个Animal的类,然后将miao扩展函数放置在Animal类中,会是怎样的一个效果呢?
它变成了Animal类的一个非静态成员,你也只能在Animal的内部调用这个扩展函数。
在对象中使用扩展函数和前面的两种情况差不多,在这里就不重复叙述了(在kotlin中的对象,object class实质就是一个普通的类,不过是通过单例模式来实现的)。
最后,我们进行下总结:
我们通过Cat().miao()这样的方式来调用扩展函数,点号前面的Cat()对象并不作为miao这个函数的拥有者,而是它的一个参数。这也很好的解释了为什么在扩展函数中能够调用被扩展的类中的非私有成员。
不知道大家有没有听过合成复用原则,由于继承是一种强耦合的关系,应该尽量少用。在kotlin中就是通过合成来实现扩展的。