Kotlin 支持后备属性和委托属性,它们允许你在不直接使用字段的情况下管理属性。
1、后备属性
| 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) { … } // ❌ 不能写
| 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() 自定义块(已经被委托接管)。
网硕互联帮助中心





评论前必须登录!
注册