在Kotlin中,住了主构造函数和次构造函数外,还给我们提供了init代码块,供我们做一些初始化操作。
那么kotlin init代码块是什么时候执行的呢,和构造方法以及伴生对象一起使用时它们的执行顺序又是怎样的呢?
下面我们通过一个小例子来看一下
我们先来看看kotlin中init代码块和构造方法的执行顺序
class Person {
/*主构造*/
constructor() {
println("constructor1")
}
/*属性*/
private var gender: Boolean = true
/*次构造方法*/
constructor(name: String, age: Int) : this() {
println("constructor2")
}
/*初始化代码块*/
init {
println("Person init 2,gender:${gender}")
}
/*初始化代码块*/
init {
println("Person init 1")
}
}
上面是一个简单的Person类,我们在类中写了一个主构造方法、一个次构造方法、两个init代码块和一个属性
此时,当我们调用Person()
时,Person类中代码的执行顺序是什么样的呢
打印结果如下
Person init 2,gender:true
Person init 1
constructor1
可以看到,首先会按顺序执行类中init代码块,然后再执行构造方法里代码,并且我可以在init代码块中使用类声明的属性
下面我们来看一下翻译成java的代码
查看kotlin代码编译成java代码的方法
下面是由kotlin代码编译成java代码后的图示
可以看到,实际上init代码块中的方法会按顺序放在主构造函数中,主构造函数中原来的代码会在后面执行。
假如我们执行Person("yzq",100)
这个呢。
根据上面编译过后的java代码来看,执行顺序应该就很清楚了吧,这里我就不把结果放出来了,可以自己思考一下。然后自己执行以下看看结果。
我们再来给类中加入个伴生对象来看看
class Person {
/*主构造*/
constructor() {
println("constructor1")
}
/*属性*/
private var gender: Boolean = true
/*伴生对象*/
companion object {
val instance: Person by lazy {
Person("yzq", 26)
}
/*伴生对象中的初始化代码*/
init {
println("companion init 1")
}
init {
println("companion init 2")
}
}
/*次构造方法*/
constructor(name: String, age: Int) : this() {
println("constructor2")
}
/*初始化代码块*/
init {
println("Person init 2,gender:${gender}")
}
/*初始化代码块*/
init {
println("Person init 1")
}
}
加入伴生对象后,那肯定就是伴生对象中的代码先执行了。
我们调用一下Person.instance
来看一下结果
companion init 1
companion init 2
Person init 2,gender:true
Person init 1
constructor1
constructor2
和你预想的一样吗?
首先伴生对象中的代码是在类加载时就会执行。也就说当我们代码中出现Person这个单词的时候,伴生对象中的代码就开始执行了,此时会先顺序的执行伴生对象中的init代码块,但是由于instance是懒加载的,所以只有当我们代码出现Person.instance时,才会执行instance中委托的代码。
此时会去调用次构造函数,但是调用次构造函数之前会先去调用主构造函数,而执行主构造函数时就和最上面的那种执行顺序是一致的了,先执行类中的init代码块,再执行主构造函数原本的代码,主构造函数调用完毕后,最后调用次构造函数中的代码。
下面我们来看一下编译过后的java代码
public final class Person {
private boolean gender;
@NotNull
private static final Lazy instance$delegate;
public static final Person.Companion Companion = new Person.Companion((DefaultConstructorMarker)null);
public Person() {
this.gender = true;
String var1 = "Person init 2,gender:" + this.gender;
System.out.println(var1);
var1 = "Person init 1";
System.out.println(var1);
var1 = "constructor1";
System.out.println(var1);
}
public Person(@NotNull String name, int age) {
Intrinsics.checkParameterIsNotNull(name, "name");
this();
String var3 = "constructor2";
System.out.println(var3);
}
static {
instance$delegate = LazyKt.lazy((Function0)null.INSTANCE);
String var0 = "companion init 1";
System.out.println(var0);
var0 = "companion init 2";
System.out.println(var0);
}
@Metadata(
mv = {1, 1, 13},
bv = {1, 0, 3},
k = 1,
d1 = {"\u0000\u0014\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0018\u0002\n\u0002\b\u0005\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002R\u001b\u0010\u0003\u001a\u00020\u00048FX\u0086\u0084\u0002¢\u0006\f\n\u0004\b\u0007\u0010\b\u001a\u0004\b\u0005\u0010\u0006¨\u0006\t"},
d2 = {"LPerson$Companion;", "", "()V", "instance", "LPerson;", "getInstance", "()LPerson;", "instance$delegate", "Lkotlin/Lazy;", "kotlin"}
)
public static final class Companion {
// $FF: synthetic field
static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(Reflection.getOrCreateKotlinClass(Person.Companion.class), "instance", "getInstance()LPerson;"))};
@NotNull
public final Person getInstance() {
Lazy var1 = Person.instance$delegate;
KProperty var3 = $$delegatedProperties[0];
return (Person)var1.getValue();
}
private Companion() {
}
// $FF: synthetic method
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
现在,init代码块和构造方法以及伴生对象中代码的执行顺序清楚了吗?
如果你觉得本文对你有帮助,麻烦动动手指顶一下,算是对本文的一个认可,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!