Kotlin既有主构函数,又有次构函数,还有一个init函数,甚至还有属性的初始化,这些执行的顺序是怎么样的,一起来看看。
测试开始
测试类
怎么在主构函数,和属性初始化执行时,打印一个结果呢?毕竟这二者是没有函数体的。我的答案是Kotlin的also函数,这样就可以在执行的同时打印一个log了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class ClassTest constructor( value: Int = 1.also { Log.d("test", "主构函数") } ) { constructor(value: String): this(3) { Log.d("test", "次构函数") } val value2 = 2.also { Log.d("test", "初始化变量$it") } init { Log.d("test", "init函数,value = $value") }
val value3 = 3.also { Log.d("test", "初始化变量$it") } }
|
从主构函数开始
我们在代码中使用主构函数构造ClassTest类。
看看测试结果
1 2 3 4
| D/test: 主构函数 D/test: 初始化变量1 D/test: init函数,value = 1 D/test: 初始化变量2
|
可以看到顺序是:主构函数->初始化变量->init函数。
可以看到顺序是:主构函数->初始化变量1->init函数->初始化变量。
从次构函数开始
我们再看看次构函数,通过次构函数开始构造ClassTest类。
看看测试结果
1 2 3 4
| D/test: 初始化变量1 D/test: init函数,value = 3 D/test: 初始化变量2 D/test: 次构函数
|
没有打印出主构函数,这个结果很正常,因为这次我们是通过次构函数去构造的类,虽然它在仍然调用了主构函数,但是没有去调用我们的1.also { Log.d("test", "主构函数") } ,这个默认值被覆盖掉了。
虽然没有主构函数的打印,但是根据前面的测试,我们可以猜测顺序应该是:主构函数->初始化变量->init函数->次构函数。
反编译
将ClassTest反编译成java代码可以看的跟清楚。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| public final class ClassTest { private final int value2; private final int value3; public final int getValue2() { return this.value2; }
public final int getValue3() { return this.value3; } public ClassTest(int value) { byte var2 = 2; int var4 = false; Log.d("test", "初始化变量" + var2); Unit var6 = Unit.INSTANCE; this.value2 = var2; Log.d("test", "init函数,value = " + value); var2 = 3; var4 = false; Log.d("test", "初始化变量" + var2); var6 = Unit.INSTANCE; this.value3 = var2; } public ClassTest(int var1, int var2, DefaultConstructorMarker var3) { if ((var2 & 1) != 0) { byte var4 = 1; int var6 = false; Log.d("test", "主构函数"); var1 = var4; } this(var1); } public ClassTest() { this(0, 1, (DefaultConstructorMarker)null); }
public ClassTest(@NotNull String value) { Intrinsics.checkNotNullParameter(value, "value"); this(3); Log.d("test", "次构函数"); } }
|
从上面反编译的java代码可以清晰的看到各个过程的执行顺序,与我们猜测的结论是符合的。
结论
Kotlin构造函数、init函数和属性初始化的顺序是以下顺序:
主构函数->初始化变量->init函数->次构函数
主构函数->(初始化变量和init函数)->次构函数。
初始化变量和init函数的顺序与代码编写的顺序一致。
因此要将初始化变量放在init函数之前,否则可能导致init函数中调用的变量值为空。