Kotlin: Kotlin委托与属性委托详解
最后更新:2026-08-26
委托是 Kotlin 的杀手级语言特性—— 一行实现线程安全延迟初始化, 一行监听属性变化。Charlie 用类委托替代装饰器模式的样板代码,用属性委托让 OrderProcessor 的配置属性自动持久化。
1. 你将学到
- 类委托 :装饰器模式零样板
- :线程安全延迟计算
- / :属性变更监听与拦截
- 自定义属性委托:
- Charlie 实战: +
2. 一个架构师的真实故事
(1) 痛点:装饰器模式样板代码
Charlie 用 Java 装饰器模式给 添加日志和缓存,每加一层就要写 20+ 行委托方法——接口有 8 个方法,3 层装饰器就是 480 行纯委托代码。
(2) Kotlin 类委托的解法
KOTLIN
// Java: 20+ lines per method per decorator
class LoggedRepository implements OrderRepository {
private final OrderRepository delegate;
@Override public Order findById(String id) { log(); return delegate.findById(id); }
// ... repeat for all 8 methods
}
// Kotlin: 1 line delegates ALL methods
class LoggedRepository(
private val delegate: OrderRepository
) : OrderRepository by delegate // Auto-delegates all methods!
一行代码替代 20+ 行方法委托——只 override 需要增强的方法。
3. 类委托(by)
(1) 基本语法
KOTLIN
interface OrderRepository {
fun findById(id: String): Order?
fun save(order: Order)
fun findAll(): List`<Order>`
}
class InMemoryRepository : OrderRepository {
private val storage = mutableMapOf<String, Order>()
override fun findById(id: String) = storage[id]
override fun save(order: Order) { storage[order.id] = order }
override fun findAll() = storage.values.toList()
}
// Class delegation: auto-delegates all interface methods
class LoggedRepository(
private val inner: OrderRepository
) : OrderRepository by inner { // All methods delegated!
// Override only what you want to enhance
override fun save(order: Order) {
println("Saving order: ${order.id}")
inner.save(order) // Call delegate explicitly
}
}
(2) 多层委托
KOTLIN
// Layer 1: Caching
class CachedRepository(
private val delegate: OrderRepository
) : OrderRepository by delegate {
private val cache = mutableMapOf<String, Order>()
override fun findById(id: String): Order? {
return cache.getOrPut(id) { delegate.findById(id)!! }
}
}
// Layer 2: Logging
class LoggedRepository(
private val delegate: OrderRepository
) : OrderRepository by delegate {
override fun save(order: Order) {
println("Saving: ${order.id}")
delegate.save(order)
}
}
// Compose: logging + caching + in-memory
val repo = LoggedRepository(CachedRepository(InMemoryRepository()))
(3) 类委托 vs 继承 vs 装饰器
| 维度 | 继承 | 装饰器(Java) | 类委托(Kotlin) |
|---|---|---|---|
| 代码量 | 少 | 多(样板) | 少(by) |
| 灵活性 | 单继承 | 多层组合 | 多层组合 |
| 耦合度 | 高 | 低 | 低 |
| 运行时 | 父类绑定 | 委托对象绑定 | 委托对象绑定 |
4. 属性委托:by lazy
(1) 基本用法
KOTLIN
class OrderProcessor {
// Lazy: computed on first access, thread-safe by default
val cache: OrderCache by lazy {
println("Initializing cache...")
OrderCache(maxSize = 10_000)
}
// Lazy with custom thread safety
val heavyConfig by lazy(LazyThreadSafetyMode.PUBLICATION) {
loadConfig() // May be called multiple times, but only first result used
}
}
(2) lazy 线程安全模式
| 模式 | 行为 | 性能 | 适用场景 |
|---|---|---|---|
| (默认) | 单次初始化,双重检查锁 | 稍慢 | 多线程环境 |
| 多线程可能计算多次,只取首次结果 | 较快 | 初始化无副作用 | |
| 无同步保障 | 最快 | 确定单线程 |
5. 属性委托:observable / vetoable
(1) observable — 变更通知
KOTLIN
import kotlin.properties.Delegates
class Order(var id: String) {
// observable: notify on every change
var status: String by Delegates.observable("PENDING") { _, old, new ->
println("Status changed: $old -> $new")
}
}
val order = Order("ORD-001")
order.status = "CONFIRMED" // Prints: Status changed: PENDING -> CONFIRMED
order.status = "SHIPPED" // Prints: Status changed: CONFIRMED -> SHIPPED
(2) vetoable — 变更拦截
KOTLIN
class Order(var id: String) {
// vetoable: can reject the change
var total: Double by Delegates.vetoable(0.0) { _, old, new ->
if (new < 0) {
println("Rejecting negative total: $new")
false // Veto: keep old value
} else {
true // Accept the change
}
}
}
val order = Order("ORD-001")
order.total = 299.99 // Accepted
order.total = -50.0 // Rejected: prints warning, keeps 299.99
(3) observable vs vetoable 对比
| 委托 | 行为 | 返回值 | 适用场景 |
|---|---|---|---|
| 通知变更,不拦截 | Unit | 日志/审计 | |
| 可拦截变更 | Boolean | 验证/约束 |
6. 自定义属性委托
(1) ReadWriteProperty 接口
KOTLIN
import kotlin.reflect.KProperty
class PersistentProperty`<T>`(
private val key: String,
private val defaultValue: T
) : ReadWriteProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
// Load from persistence (simplified)
return loadFromStore(key) ?: defaultValue
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
// Save to persistence
saveToStore(key, value)
}
}
// Simulated persistence
private val store = mutableMapOf<String, Any?>()
private fun `<T>` loadFromStore(key: String): T? = store[key] as T?
private fun `<T>` saveToStore(key: String, value: T) { store[key] = value }
(2) 使用自定义委托
KOTLIN
class OrderConfig {
var maxRetries: Int by PersistentProperty("order.max_retries", 3)
var timeout: Long by PersistentProperty("order.timeout", 5_000L)
var taxRate: Double by PersistentProperty("order.tax_rate", 0.08)
}
val config = OrderConfig()
println(config.maxRetries) // 3 (default)
config.maxRetries = 5
println(config.maxRetries) // 5 (persisted)
(3) 委托类图
classDiagram
class ReadOnlyProperty {
<<interface>>
+getValue(thisRef, property): T
}
class ReadWriteProperty {
<<interface>>
+getValue(thisRef, property): T
+setValue(thisRef, property, value: T)
}
class LazyDelegate {
+getValue(): T
}
class ObservableDelegate {
+setValue()
}
class VetoableDelegate {
+setValue()
}
class CustomDelegate {
+getValue()
+setValue()
}
ReadOnlyProperty <|-- ReadWriteProperty
ReadOnlyProperty <|-- LazyDelegate
ReadWriteProperty <|-- ObservableDelegate
ReadWriteProperty <|-- VetoableDelegate
ReadWriteProperty <|-- CustomDelegate
7. 完整示例:OrderProcessor 委托架构
KOTLIN
// ============================================
// OrderProcessor - Delegation Architecture
// Feature: Class delegation + property delegation
// ============================================
import kotlin.properties.Delegates
import kotlin.reflect.KProperty
// --- Class Delegation ---
interface OrderRepository {
fun findById(id: String): Order?
fun save(order: Order)
fun findAll(): List`<Order>`
}
data class Order(val id: String, var total: Double, var status: String, val customer: String)
class InMemoryRepository : OrderRepository {
private val storage = mutableMapOf<String, Order>()
override fun findById(id: String) = storage[id]
override fun save(order: Order) { storage[order.id] = order }
override fun findAll() = storage.values.toList()
}
class LoggedRepository(private val inner: OrderRepository) : OrderRepository by inner {
override fun save(order: Order) {
println(" [LOG] Saving order: ${order.id} (\$${order.total} USD)")
inner.save(order)
}
}
class ValidatedRepository(private val inner: OrderRepository) : OrderRepository by inner {
override fun save(order: Order) {
require(order.total >= 0) { "Total must be non-negative" }
require(order.id.startsWith("ORD-")) { "Invalid order ID format" }
inner.save(order)
}
}
// --- Property Delegation ---
private val configStore = mutableMapOf<String, Any?>()
class ConfigProperty`<T>`(private val key: String, private val default: T) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
@Suppress("UNCHECKED_CAST")
return configStore[key] as? T ?: default
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
configStore[key] = value
println(" [CONFIG] $key = $value")
}
}
class OrderProcessorConfig {
var maxOrderValue: Double by ConfigProperty("max_order_value", 100_000.0)
var defaultTaxRate: Double by ConfigProperty("default_tax_rate", 0.08)
var enableAuditing: Boolean by Delegates.observable(true) { _, _, new ->
println(" [CONFIG] Auditing ${if (new) "enabled" else "disabled"}")
}
var processingTimeout: Long by Delegates.vetoable(30_000L) { _, _, new ->
if (new < 1_000) { println(" [CONFIG] Rejecting timeout < 1000ms"); false }
else true
}
}
fun main() {
// Compose: validation + logging + in-memory
val repo: OrderRepository = ValidatedRepository(LoggedRepository(InMemoryRepository()))
println("=== Saving Orders ===")
repo.save(Order("ORD-001", 299.99, "CONFIRMED", "Alice"))
repo.save(Order("ORD-002", 15_000.00, "PENDING", "Bob"))
println("\n=== Find Order ===")
println(repo.findById("ORD-001"))
// Property delegation
println("\n=== Configuration ===")
val config = OrderProcessorConfig()
println("Max order value: \$${config.maxOrderValue} USD")
config.defaultTaxRate = 0.10
config.enableAuditing = false
config.processingTimeout = 500 // Rejected
config.processingTimeout = 60_000 // Accepted
println("Timeout: ${config.processingTimeout}ms")
}
输出:
TEXT
📖 仅展示
=== Saving Orders ===
[LOG] Saving order: ORD-001 ($299.99 USD)
[LOG] Saving order: ORD-002 ($15000.0 USD)
=== Find Order ===
Order(id=ORD-001, total=299.99, status=CONFIRMED, customer=Alice)
=== Configuration ===
Max order value: $100000.0 USD
[CONFIG] default_tax_rate = 0.1
[CONFIG] Auditing disabled
[CONFIG] Rejecting timeout < 1000ms
[CONFIG] processingTimeout = 60000
Timeout: 60000ms
❓ 常见问题
Q 类委托和继承有什么区别?
A 类委托是组合(has-a),继承是子类型化(is-a)。委托更灵活——可以运行时替换委托对象,且避免继承的紧耦合。
Q by lazy 初始化一定会发生吗?
A 不一定。如果从未访问该属性,初始化就不会执行。这是 lazy 的核心优势——避免不必要的初始化开销。
Q 属性委托的性能如何?
A 每次读写属性会多一次函数调用(getValue/setValue),但 JVM 的内联优化通常消除这个开销。热点代码可测量后决定。
Q 一个类可以用多个 by 委托吗?
A 类委托只能委托一个接口( 可以委托多个),属性委托每个属性独立委托。
Q observable 能用来做响应式编程吗?
A 功能有限,只在本对象内通知。跨组件响应式请用 StateFlow/SharedFlow。observable 更适合审计日志和简单通知。
Q 自定义委托必须实现 ReadWriteProperty 吗?
A 不是必须的,只要提供 (和 对于 var)即可。ReadWriteProperty 只是便利接口。
📖 小节
- 类委托 自动委托接口所有方法,装饰器模式零样板
- 线程安全延迟初始化,适合重资源对象的按需创建
- 监听属性变更, 可拦截变更
- 自定义属性委托只需实现 / 运算符函数
- 委托优先于继承——组合比继承更灵活
- 多层委托组合实现关注点分离:日志 + 缓存 + 验证
📝 作业
- 基础题(难度⭐):用 实现一个延迟初始化的数据库连接,首次访问时打印"Connecting..."。提示:
- 进阶题(难度⭐⭐):用类委托实现 ,只 override 方法添加日志。提示:
- 挑战题(难度⭐⭐⭐):实现自定义属性委托 ,每次读写打印调用栈中的属性名和值。提示: +