Kotlin: Kotlin委托与属性委托详解

最后更新:2026-08-26

委托是 Kotlin 的杀手级语言特性—— 一行实现线程安全延迟初始化, 一行监听属性变化。Charlie 用类委托替代装饰器模式的样板代码,用属性委托让 OrderProcessor 的配置属性自动持久化。

1. 你将学到


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) 委托类图

100%
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 只是便利接口。

📖 小节


📝 作业

  1. 基础题(难度⭐):用 实现一个延迟初始化的数据库连接,首次访问时打印"Connecting..."。提示:
  2. 进阶题(难度⭐⭐):用类委托实现 ,只 override 方法添加日志。提示:
  3. 挑战题(难度⭐⭐⭐):实现自定义属性委托 ,每次读写打印调用栈中的属性名和值。提示: +

← 上一课 | 下一课 →

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏