Kotlin的主构造函数,次构造函数,init函数执行顺序(存在继承关系)

327 阅读2分钟
先说结论

执行顺序:父主构造 -> 父类init -> 父次构造 -> 子主构造 -> 子init -> 子次构造

代码示例

创建了两个类,Child继承Parent

open class Parent(var name: String, var age: Int) {
    constructor(name: String) : this(name, 30) {
        println("Parent constructor")
    }
    init {
        println("Parent init")
    }
}
class Child(name: String, age: Int) : Parent(name, age) { //调用父类的主构造函数
    constructor(name: String) : this(name, 10) { 
        println("Child constructor")
    }
    init {
        println("Child init")
    }
}
fun main(){
    val child1 = Child("James") //通过子类次构造函数创建对象
    val child2 = Child("Harden", 12) //通过子类主构造函数创建对象
}

运行结果

Parent init 
Child init
Child constructor
============
Parent init
Child init

可以看出父类代码总是在子类之前执行。反编译一下代码看看

反编译
public class Parent {
   public Parent(@NotNull String name, int age) {
      Intrinsics.checkNotNullParameter(name, "name");
      super();
      this.name = name;
      this.age = age;
      String var3 = "Parent init"; //init代码块直接在移到了主构造函数之后
      System.out.println(var3);
   }
   public Parent(@NotNull String name) {
      Intrinsics.checkNotNullParameter(name, "name");
      this(name, 30); //调用主构造函数
      String var2 = "Parent constructor"; //子构造函数内的代码
      System.out.println(var2);
   }
}
public final class Child extends Parent {
   public Child(@NotNull String name, int age) {
      Intrinsics.checkNotNullParameter(name, "name");
      super(name, age);
      String var3 = "Child init"; //init代码块直接在移到了主构造函数之后
      System.out.println(var3);
   }

   public Child(@NotNull String name) {
      Intrinsics.checkNotNullParameter(name, "name");
      this(name, 10);
      String var2 = "Child constructor";
      System.out.println(var2);
   }
}

反编译代码可以看出来,在java代码中直接将init代码块拷贝到了主构造函数最后,也就是主构造函数执行完就会执行init,而在次构造函数又调用了主构造函数,所以执行顺序是主构造函数->init代码块->次构造函数

在例子val child1 = Child("James")中,通过子类次构造函数创建对象,会先调用子类的主构造函数,在子类的主构造函数中会调用父类的主构造函数,而init正是在主构造函数执行完之后调用的。所以打印了

Parent init 
Child init
Child constructor

如果子类调用的父类的次构造函数呢?也就是说把代码class Child(name: String, age: Int) : Parent(name, age)改为class Child(name: String, age: Int) : Parent(name)。打印结果如下:

Parent init
Parent constructor
Child init
Child constructor

可以看出父类的主构造函数,init函数,次构造函数总是比子类的先执行。