Kotlin属性带与不带setter时的初始化问题
Kotlin属性机制常见疑问解答
我有Java开发背景,正在学习Kotlin的属性机制,先看几个代码示例:
初始错误示例
class Rectangle (var width: Double, var height: Double) { var area: Double // ERROR: must be initialized get() = width * height }
根据之前的学习,带幕后字段(backing field)的属性必须初始化,上述示例中area会生成幕后字段,因此编译器提示必须初始化。
初始解决方法
在初始化块中初始化area可以解决错误:
class Rectangle (var width: Double, var height: Double) { var area: Double get() = width * height init { area = width * height } }
也可以在声明时直接初始化:
var area: Double = width * height
添加Setter后的新问题
但如果给area属性添加setter,即使初始化块里已经赋值,编译器还是会提示必须初始化:
class Rectangle (var width: Double, var height: Double) { var area: Double // ERROR: must be initialized get() = width * height set(value) { field = value } init { area = width * height } }
疑问解答
1. 为什么添加setter后编译器会报错?
当你自定义setter并在其中使用field引用幕后字段时,Kotlin编译器对幕后字段的初始化时机有更严格的要求:必须在属性声明时或者主构造函数的初始化列表中完成初始化,不能依赖init块或次构造函数。
2. area已在初始化块中初始化,为何仍报错?
这是因为编译器需要确保在任何setter调用(包括构造过程中可能的调用)发生前,幕后字段已经完成初始化。init块是在主构造函数执行完毕后才运行的,理论上存在次构造函数先调用setter、再执行init块的可能,编译器无法保证此时幕后字段已就绪,因此即使init块里有赋值,也会报错。
3. 属性带setter与不带setter有何区别?
- 不带自定义setter的var属性:编译器会自动生成默认setter,直接操作幕后字段。你可以通过赋值语句(比如
area = 10.0)修改幕后字段的值,getter则使用你自定义的逻辑。 - 带自定义setter的var属性:你可以完全控制属性赋值时的逻辑,比如校验输入值、同步更新其他属性等。如果setter中使用
field,则依赖幕后字段存储值;如果不使用field,则属于无幕后字段的计算属性——这种情况下必须同时自定义get和set,且所有逻辑都要基于其他属性或外部状态,没有自己的存储。
内容的提问来源于stack exchange,提问作者Yousaf
相关产品推荐
相关产品推荐

