Kotlin 支持后备属性和委托属性,它们允许你在不直接使用字段的情况下管理属性。
1、后备属性
| 修饰符 | getter | setter | backing field |
|---|---|---|---|
| val | ✅ 有 | ❌ 无 | 视情况有 |
| var | ✅ 有 | ✅ 有 | 视情况有 |
backing field(幕后字段,即 field)只在 getter/setter 中实际访问了它时才会生成;如果 getter 是纯计算(如 get() = x + 1 且没读 field),编译器就不会分配那个存储字段。
var name: String = "" get() = field // ✅ 读那块存储 set(v) { field = v } // ✅ 写那块存储 // field 只在 getter/setter 内部可用,不是关键字,是一个上下文相关的隐式变量。 // var:可同时定义 get + set var name: String = "" get() = field.uppercase() set(value) { field = value.trim() } // val:只能定义 get,写 set 会编译错误 val id: Long = 0L get() = field // set(v) { ... } // ❌ 不能写| 写法 | 有 backing field? | 原因 |
|---|---|---|
var x: Int = 0 | ✅ | var 必须能存,要存就得有字段 |
val x: Int = 0 | ✅ | val 初始化后值固定,要存初始值 |
val x: Int get() = 42 | ❌ | getter 不依赖任何存储,纯计算 |
val x: Int get() = y + 1// 每次读都计算 | ❌ | 同上,读别人的字段 |
var x: Int = 0; get() = field | ✅ | setter 隐式用 field,必须存 |
var x: Int get() = compute(); set(v) { cache = v } | ❌ | get/set 都没用 field,没字段 |
Backing field = 属性背后那块真正装数据的私有字段;field 是在 getter/setter 内引用它的唯一名字;不是所有属性都有 backing field,纯计算的属性和被委托的属性都没有。
class Demo { // 1. 有 backing field var count: Int = 0 set(v) { field = if (v < 0) 0 else v } // 写入校验 // 2. 无 backing field(派生属性) val isPositive: Boolean get() = count > 0 // 3. 无 backing field(转发到别的对象) val displayCount: Int get() = count // 每次读 count 的当前值 // 4. 有 backing field(默认 getter/setter) val id: Long = System.currentTimeMillis() }反编译成 Java 后大致是:
class Demo { private int count; // ← backing field private final long id; // ← backing field // isPositive、displayCount 没有字段,只有方法 public int getCount() { return count; } public void setCount(int v) { this.count = v < 0 ? 0 : v; } public boolean isPositive() { return getCount() > 0; } public int getDisplayCount() { return getCount(); } // get()相当于实现了get方法 public long getId() { return id; } }2、委托属性
委托属性(另一种声明方式)例如,使用by lazy()创建的属性就是一种委托属性,它在第一次访问时才计算其值。
val x: Int by lazy { compute() } var y: String by Delegates.observable("") { _, old, new -> ... } var z: String by map // 委托给 Map用 by 把 get/set 完全交给委托对象,此时不能写 get()/set() 自定义块(已经被委托接管)。