如何在父类构造函数执行前初始化子类重写的属性?
解决Kotlin父类init块执行时子类重写属性未初始化的问题
先还原问题对应的示例代码:
open class Base { open val defaultPrefix: String? = null val word: String init { word = "$defaultPrefix ipsum" } } class ClassA : Base() { override val defaultPrefix: String = "foobar" } fun main() { println(ClassA().word) // 输出:null ipsum }
问题根源是Kotlin的初始化顺序:父类init块执行早于子类属性初始化,导致父类初始化时子类重写的属性还处于默认值(null)状态。以下是几种可行的解决方案:
方案1:通过父类构造函数传递参数(最推荐)
将需要在父类初始化阶段使用的属性,作为父类构造函数的参数传入,确保父类init块执行前参数已被子类传入并赋值,从根源规避初始化顺序问题。
修改后的最简代码:
open class Base(open val defaultPrefix: String?) { val word: String init { word = "${defaultPrefix ?: ""} ipsum" } } class ClassA : Base("foobar") fun main() { println(ClassA().word) // 输出:foobar ipsum }
如果子类需要单独维护defaultPrefix属性,也可以显式重写:
open class Base(prefix: String?) { val defaultPrefix: String? = prefix val word: String init { word = "${defaultPrefix ?: ""} ipsum" } } class ClassA : Base("foobar") { override val defaultPrefix: String = "foobar" }
方案2:使用lazy延迟初始化父类的依赖属性
让父类的word延迟初始化,直到子类属性完成赋值后再计算值,避开父类init块的执行时机限制。
代码示例:
open class Base { open val defaultPrefix: String? = null val word: String by lazy { "${defaultPrefix ?: ""} ipsum" } } class ClassA : Base() { override val defaultPrefix: String = "foobar" } fun main() { println(ClassA().word) // 输出:foobar ipsum }
注意:此方案会让word变为延迟加载属性,第一次访问时才会计算值,若需要属性在实例化时就完成初始化,该方案不适用。
方案3:用open函数替代open属性
将defaultPrefix改为open函数,Kotlin中函数的重写遵循动态绑定,父类init块调用函数时会直接调用子类的实现,此时子类函数可以返回正确的值。
代码示例:
open class Base { open fun getDefaultPrefix(): String? = null val word: String init { word = "${getDefaultPrefix() ?: ""} ipsum" } } class ClassA : Base() { override fun getDefaultPrefix(): String = "foobar" } fun main() { println(ClassA().word) // 输出:foobar ipsum }
内容的提问来源于stack exchange,提问作者Eydamos
相关产品推荐
相关产品推荐

