Kotlin显式与隐式get()/set()用法及移除get()异常问题解析
Kotlin中显式定义get()/set()属性访问器的原因
Kotlin会为普通属性自动生成默认的get/set访问器,只有当默认逻辑不符合需求时才需要显式自定义,核心适用场景如下:
- 动态计算属性值:如果属性不需要固定存储值,返回结果依赖其他属性的实时状态,自定义get()可以让属性每次被访问时都基于最新状态计算结果,不会在对象初始化时把值固定死。
- 拦截赋值操作:自定义set()可以在属性被赋值时做合法性校验、触发联动逻辑(比如状态同步、日志上报),也可以单独修改setter的可见性,比如实现“外部只读、内部可修改”的效果。
对应代码取值异常的根因
移除formattedDate的显式get()定义后,该属性会变成带后端存储字段(backing field)的普通属性,计算逻辑会在对象初始化阶段仅执行一次,结果被固定存到字段里,后续不会更新:
- 对象初始化时,主构造参数会先使用定义的默认值,此时
publishTime是空字符串"",formattedDate会基于这个空值完成计算。 - 如果使用Gson、Moshi等常见JSON反序列化库,实例创建流程是先调用无参构造生成空对象(所有参数取默认值),再通过反射逐个给属性赋值。这种场景下
formattedDate会在无参构造阶段就完成空值计算,后续publishTime被反射赋值为正确值时,formattedDate不会重新计算,会一直返回初始化时的空结果。
两种写法的逻辑差异参考如下代码:
data class SampleDomain( val publishTime: String = "" ) { // 无自定义get的写法:初始化时计算1次,结果固定,后续publishTime变更不影响该值 // val formattedDate: String = LocalDate.parse(publishTime, DateTimeFormatter.ISO_OFFSET_DATE_TIME) // .format(DateTimeFormatter.ofPattern("MMM. dd, yyyy")) // 带自定义get的写法:不生成后端存储字段,每次访问都读取最新publishTime实时计算 val formattedDate: String get() = LocalDate.parse(publishTime, DateTimeFormatter.ISO_OFFSET_DATE_TIME).format( DateTimeFormatter.ofPattern("MMM. dd, yyyy") ) }
补充规则:如果自定义get()的属性没有初始化赋值、访问器内也没有操作
field关键字,Kotlin不会为该属性分配单独的内存空间,访问时直接执行计算逻辑返回结果,没有额外内存开销。
内容的提问来源于stack exchange,提问作者Bitwise DEVS
相关产品推荐
相关产品推荐

