kotlin的属性拓展和方法拓展能够扩展一个类的新功能而无需继承该类或者使用像装饰者这样的设计模式,但是他并不是打破原来类的封闭性和完整性,而是一种语法糖。并且属性拓展和方法拓展在原理上其实是一样的,反编译为Java后可以看到两者都是通过静态方法实现的。只要记住这一点再去理解拓展的很多特性就很容易了。但是实际工作中会遇到类的继承、方法重写、多态等实际问题,当这些问题和拓展糅合在一起的时候,还是值得去学习的。
拓展的原理
/**
* 基础类
*/
open class Car
/**
* 拓展方法
*
*/
fun Car.play() {
println("car play")
}
/**
* 拓展属性
*/
val Car.name: String
get() = "car"
转换为Java后:
/**
* 拓展方法转换为静态方法
*/
public static final void play(@NotNull Car $this$play) {
String var1 = "car play";
System.out.println(var1);
}
/**
* 拓展属性转换为静态方法
*/
public static final String getName(@NotNull Car $this$name) {
return "car";
}
拓展的原理就是Java中的静态方法。拓展函数直接转换为静态方法,并将被拓展的类作为拓展方法的第一个参数。拓展属性则是直接转化为静态的get、set方法,并将被拓展的类作为第一个参数。所以拓展属性是不能保存状态的,即不能直接初始化赋值,必须提供自定义的get、set方法来操作值。
拓展方法和拓展属性可以继承吗
class Audi : Car()
fun test() {
Audi().play() //输出: car play
println(Audi().name) //输出:car
}
拓展方法和拓展属性是可以继承的。子类可以调用父类的拓展方法和拓展属性,原因也很简单,拓展方法的原理就是静态方法,子类在调用父类的拓展方法时,相当于把自己当做第一个参数直接调用静态方法,那当然是可以的了。那拓展方法可以重写吗?
拓展方法可以重写吗
class Audi : Car()
fun Audi.play() {
println("Audi play")
}
fun test() {
Audi().play() //输出:Audi play
}
我在子类中也定义了一个相同的拓展方法,输出的结果也变成了子类的实现,这是不是意味着拓展方法可以重写呢?答案是否定的。想想拓展方法的实现原理,当为子类定义了一个拓展方法时,相当于又多了一个以子类作为第一个参数的静态方法,子类的play方法和父类的play方法实际上是两个不同的静态方法,所以这并不是重写。相同的道理,拓展属性也是一样的。
多态
想象这样的场景:
fun drive(car: Car) {
car.play()
}
fun test() {
drive(Car()) //输出: car play
drive(Audi()) //输出:car play ,注意这里并不是 Audi play
}
当多态和拓展方法遇到一起的时候就更有意思了,如果play是普通的类方法,那么第二个输出应该是 Audi play,这也是熟悉Java开发的惯性思维。但是拓展方法就不一样了,即使第二个drive方法给出了Audi的实例对象,但最终调用的还是Car的play方法。其实很简单,Car 和 Audi 的两个拓展方法play会生成不同的两个静态方法,所以在drive方法中调用的是 Car 生成的静态方法,并不会因为多态而变成 Audi 生成的静态方法。这也是符合Java的逻辑的。