Kotlin中init块内的name引用为何指向构造函数参数而非类属性
Kotlin中init块内的name引用为何指向构造函数参数而非类属性
哈哈,这个坑我刚学Kotlin的时候也踩过,当时盯着控制台输出愣了半天!咱们先把问题里的代码理清楚,方便一步步分析:
class InitOrderDemo(name: String) { init { println("$name") } val name = name + " " + "world" } fun main() { InitOrderDemo("hello") // 实际输出:hello }
你遇到的问题核心就是Kotlin的实例初始化顺序,咱们对照规则拆解一下:
During the initialization of an instance, the initializer blocks are executed in the same order as they appear in the class body, interleaved with the property initializers
翻译成大白话就是:创建实例时,类体里的属性初始化代码和init块,是严格按照你写代码的先后顺序来执行的,谁在前谁先跑。
咱们一步步走一遍实例创建的流程:
- 调用
InitOrderDemo("hello")时,构造函数的参数name先被赋值为"hello",这个参数在整个初始化过程中都是能访问到的。 - 接下来开始执行类体里的代码,第一个碰到的就是
init块——这时候类里的val name属性还没开始初始化呢,它的定义在init块后面,得等init块执行完才会轮到它。 - 所以
init块里的println("$name"),编译器会在当前可用的作用域里找name,这时候只有构造函数传进来的那个参数name是可用的,自然就输出了"hello"。 - 等
init块跑完,才会执行val name = name + " world"这行:右边的name还是构造函数的参数,赋值完成后,类属性name才会变成"hello world",但这时候init块已经执行完了,你自然看不到这个值被打印出来。
要是把代码顺序调一下,把类属性name的定义放在init块前面:
class InitOrderDemo(name: String) { val name = name + " " + "world" init { println("$name") } }
这时候就会先初始化类属性name(用构造函数参数拼接成"hello world"),然后init块里打印的就是类属性的name,输出就变成"hello world"了。
另外提个小细节:要是你想在init块里明确指定引用类属性,得用this.name,但如果类属性还没初始化(比如init块在它前面),调用this.name会直接抛UninitializedPropertyAccessException,所以这种场景下也不能这么用,必须等类属性初始化完成才行。
内容来源于stack exchange
相关产品推荐
相关产品推荐

