Kotlin构造器内如何直接访问可变列表字段而非其自定义Getter?
问题分析与解决方案
你的问题核心在于Kotlin中属性的访问默认会调用getter方法,哪怕是在类内部的init块里也不例外。你定义的mutableList属性有一个自定义getter,它返回的是Guava的ImmutableList(不可变列表),所以当你在init块里写完mutableList = mutableListOf()(这一步是直接给幕后字段field赋值,属于初始化操作),之后调用mutableList.add(...)时,实际是调用了getter返回不可变列表,自然会抛出不支持修改的异常。
解决方法一:直接访问幕后字段field
在init块里操作可变列表时,不要用属性名mutableList,而是显式使用field来直接访问幕后的可变列表实例,跳过getter调用:
data class MutableClass(val list: List<Foo>) { private val mutableList: List<Foo> get() = ImmutableList.copyOf(field) init { // 直接给幕后字段赋值(val属性初始化阶段允许操作field) field = mutableListOf<Foo>() list.forEach { // 强制转换为MutableList后操作,因为field实际存储的是可变列表 (field as MutableList<Foo>).add(it.copy()) } } } data class Foo {}
解决方法二:拆分可变内部列表与不可变视图(推荐)
这是更符合Guava设计理念的写法:用一个下划线前缀的私有可变列表存储内部状态,再定义单独的属性返回不可变副本。类内部操作时直接使用可变列表变量,彻底避免混淆:
data class MutableClass(val list: List<Foo>) { // 私有可变列表,仅用于内部修改 private val _internalMutableList = mutableListOf<Foo>() // 提供不可变访问视图的属性,每次调用getter返回新的不可变副本 private val immutableList: List<Foo> get() = ImmutableList.copyOf(_internalMutableList) init { list.forEach { // 直接操作可变列表,完全不会触发getter _internalMutableList.add(it.copy()) } } } data class Foo {}
这种写法职责清晰:_internalMutableList专门负责内部状态修改,immutableList专门对外(或内部只读场景)提供不可变视图,从根源上避免了误调用getter的问题。
额外说明
在Kotlin中:
- 对于自定义getter的
val属性,除了初始化阶段给field赋值外,所有对属性名的访问都会触发getter方法。 field关键字仅能在属性的getter/setter以及init块中使用,用于直接访问属性的幕后存储字段。
内容的提问来源于stack exchange,提问作者Cheng
相关产品推荐
相关产品推荐

