学习过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 文件。
FeintKotlin
这张图所示的是文章开头演示代码中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中就是通过合成来实现扩展的。
扩展属性
我们在IDEA中输入以下的Kotlin代码,看看它所对应的Java代码是怎样的:
class TigerLion(val miao:String="miao",val wang:String="wang")val TigerLion.mw get() = miao+wang
当我们为某个添加一个扩展属性时,Kotlin实际并没有为这个类添加一个属性,而是生成了一个和被扩展属性相关的 get 或 set 方法。
由于扩展属性并不是真实存在的,因此,我们也就无法对其进行初始化,也无法在它的getter 和 setter中使用幕后字段 field。
当将其定义在被扩展的类的内部时,效果和扩展函数一样。相当于为这个类添加了一个私有的 get 或 set 方法。
总结:
扩展属性适用于,当你进行的某一操作需要用到多个属性或是属性的调用层级比较多时,可以适用扩展属性,相当于提供了一个简洁的调用接口,使内部复杂的细节变的透明。
共同学习,写下你的评论
评论加载中...
作者其他优质文章