在Kotlin中,init块和super的调用负责在考虑类层次结构的情况下正确初始化对象。这是基本机制,影响构造和继承的工作。
在Java中,可以通过显式调用super(...)或隐式默认调用构造函数。而在Kotlin中,情况完全不同:主构造函数和辅构造函数之间有明确的划分,init块用于初始化。
主要的复杂性在于正确理解属性何时初始化,init块何时被调用,以及构造函数的调用顺序,特别是在类继承和主/辅构造函数的组合时。
代码示例:
open class Base(val name: String) { init { println("Base init: $name") } } class Derived(name: String, val age: Int): Base(name) { init { println("Derived init: $name, $age") } } fun main() { Derived("Ivan", 25) } // 输出: // Base init: Ivan // Derived init: Ivan, 25
关键特点:
在继承链中,init块以何种顺序执行?
首先执行超类的init块,在初始化其属性之后,然后执行子类的init块,顺序是基于各自的属性。
代码示例:
open class A { init { println("A") } } class B: A() { init { println("B") } } fun main() { B() } // A -> B
在Kotlin中可以在构造函数之外调用super()吗?
不可以,超类的调用只能作为构造函数声明的一部分来提供超构造函数的参数。
在Kotlin中,辅构造函数可以不调用主构造函数吗?
不可以,所有的辅构造函数必须将调用委托给另一个辅构造函数或主构造函数。主构造函数反过来会调用超构造函数。
开发人员试图在超类的init块中访问在子类中初始化的属性:
open class A { init { println(f()) } open fun f() = "A" } class B : A() { private val s = "B" override fun f() = s }``` **优点:** - 使用多态性。 **缺点:** - 属性s在调用超类init时尚未初始化——结果为null或错误。 ## 积极示例 将初始化分开,以避免在构造函数中调用可重写的方法或属性: ```kotlin open class A { init { println("init A") } open fun f() = "A" } class B : A() { private val s = "B" override fun f() = s init { println(f()) } }``` **优点:** - 不访问未初始化的数据; - 保证初始化顺序。 **缺点:** - 为了正确初始化,代码量增加。