Kotlin中属性覆盖为何导致父类主构造函数属性值为0?附Java对比
先来看你的测试场景:
你写了这样一段Kotlin代码:
open class Car(c: Int){ open var cost: Int = c init { println("This comes First $cost") } } open class Vehicle(cc: Int) : Car(cc) { override var cost: Int = 20000 init { println("This comes Second $cost") } fun show(){ println("cost = $cost") } } fun main() { var vehicle = Vehicle(1000) vehicle.show() }
运行后输出:
This comes First 0
This comes Second 20000
cost = 20000
而当你注释掉override var cost: Int = 20000这一行后,输出变成了:
This comes First 1000
This comes Second 1000
cost = 1000
这个差异的核心,要从Kotlin的属性覆盖机制和初始化顺序说起,咱们再对比Java的行为来帮你理解:
一、Kotlin这边的核心原因
在Kotlin中,用override修饰的属性是真正的重写,而非Java中的字段隐藏。也就是说,当子类Vehicle用override var cost重写父类Car的cost属性时,父类中的cost实际上指向的是子类的这个属性实例,而非父类自己的独立字段。
而Kotlin的对象初始化顺序是这样的:
- 调用子类构造函数时,先把参数传递给父类构造函数(这里就是把1000传给
Car的c) - 进入父类的初始化流程:此时父类要给
cost赋值,但因为子类已经重写了这个属性,父类操作的其实是子类的cost属性——但此时子类的属性还没有被初始化(子类的属性初始化是在父类的init块执行完之后才会进行),所以cost此时是Int类型的默认值0 - 父类的
init块执行,打印出This comes First 0 - 父类初始化完成后,才会执行子类的属性初始化,把
cost赋值为20000 - 子类的
init块执行,打印This comes Second 20000 - 最后调用
show()方法,自然打印的是已经初始化完成的子类cost值20000
如果注释掉override那一行,子类就没有重写父类的cost,此时父类的cost是自己的独立属性,会被赋值为传入的1000,父类init块打印1000,子类继承这个属性值,所以后续输出都是1000。
二、对比Java的行为,你就能更清楚差异
在Java中,字段是不能被重写的,子类声明同名字段只是隐藏了父类的字段,二者是完全独立的两个字段:
比如把你的代码改成Java版本:
class Car { int cost; public Car(int c) { this.cost = c; System.out.println("This comes First " + cost); } } class Vehicle extends Car { int cost = 20000; public Vehicle(int cc) { super(cc); System.out.println("This comes Second " + cost); } public void show() { System.out.println("cost = " + cost); } } public class Main { public static void main(String[] args) { Vehicle vehicle = new Vehicle(1000); vehicle.show(); } }
这段Java代码的输出会是:
This comes First 1000
This comes Second 20000
cost = 20000
为什么和Kotlin的结果不一样?因为Java中父类的cost和子类的cost是两个不同的字段:
- 父类构造函数中操作的是自己的
cost字段,赋值为1000,所以打印1000 - 子类的
cost是自己的独立字段,在父类构造完成后才初始化赋值为20000,所以后续打印的是子类的字段值
这就是Kotlin和Java在属性/字段重写/隐藏上的核心差异:Kotlin的override var是真正的重写,父类会直接引用子类的属性;而Java的同名字段只是隐藏,父类和子类各有自己的字段。
内容的提问来源于stack exchange,提问作者Vikas Acharya

