Kotlin - 委托属性及常见问题

233 阅读3分钟

想象一下,委托就像是生活中的"代办"情景:

1. 属性委托 (Property Delegation)

🏠 场景一:租房管理

class Tenant {
    // 把房租交给房产中介来管理
    var rent: Int by RentAgent(1000)
}

class RentAgent(private var value: Int) {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): Int {
        println("房客查询房租")
        return value
    }
    
    operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: Int) {
        println("房客通过中介调整房租")
        value = newValue
    }
}

2. 常见的内置委托

2.1 lazy - 懒汉式加载

就像点外卖🍜:

class Restaurant {
    // 只有在第一次访问时才会真正去做这道菜
    val specialDish by lazy {
        println("开始准备特色菜...")
        "宫保鸡丁"
    }
}

fun main() {
    val restaurant = Restaurant()
    // 第一次访问,会打印"开始准备特色菜..."
    println(restaurant.specialDish)
    // 第二次访问直接返回已经做好的菜
    println(restaurant.specialDish)
}

2.2 observable - 观察者模式

像是给孩子零花钱💰:

class PocketMoney {
    var amount: Int by Delegates.observable(0) { _, old, new ->
        println("零花钱从 $old 变成了 $new")
        if (new > old) {
            println("孩子很开心!")
        } else {
            println("孩子有点失落...")
        }
    }
}

2.3 vetoable - 带验证的观察者

像是家长监管孩子玩游戏🎮:

class GameTime {
    var minutes: Int by Delegates.vetoable(0) { _, _, new ->
        when {
            new > 120 -> {
                println("今天玩太久了,不允许!")
                false
            }
            new < 0 -> {
                println("时间不能为负数!")
                false
            }
            else -> true
        }
    }
}

3. 类委托 (Class Delegation)

想象一个团队协作场景👥:

interface Worker {
    fun work()
    fun report()
}

// 实际工作者
class JuniorDeveloper : Worker {
    override fun work() = println("写代码,修Bug...")
    override fun report() = println("完成了一个小功能")
}

// 项目经理通过委托管理初级开发
class ProjectManager(private val developer: Worker) : Worker by developer {
    override fun report() {
        println("先整理一下报告格式...")
        developer.report()
        println("加上一些专业术语...")
    }
}

4. 委托的实际应用场景

4.1 单例模式实现

object DatabaseConfig by lazy {
    loadConfigFromFile()
}

4.2 ViewModel 中的状态管理

class MyViewModel : ViewModel() {
    private var _uiState by mutableStateOf(UiState())
    val uiState: UiState by _uiState
}

4.3 SharedPreferences 封装

class UserSettings(context: Context) {
    private val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
    
    var username by SharedPreferencesDelegate(prefs, "")
    var isLoggedIn by SharedPreferencesDelegate(prefs, false)
}

5. 常见问题及解决方案

5.1 内存泄漏问题

❌ 错误示范:

class MyActivity : AppCompatActivity() {
    // 可能导致内存泄漏
    private val heavyObject by lazy { HeavyObject(this) }
}

✅ 正确做法:

class MyActivity : AppCompatActivity() {
    // 使用 lifecycleScope 管理
    private val heavyObject by lazy { HeavyObject(lifecycleScope) }
}

5.2 线程安全问题

// 需要线程安全时使用 Delegates.synchronized
var sharedState by Delegates.synchronized(初始值)

5.3 性能问题

当使用大量委托时:

// 不推荐
class DataModel {
    val field1 by lazy { compute1() }
    val field2 by lazy { compute2() }
    val field3 by lazy { compute3() }
    // ... 更多委托
}

// 推荐
class DataModel {
    private val initializer by lazy {
        ComputeResult(
            compute1(),
            compute2(),
            compute3()
        )
    }
    val field1 get() = initializer.result1
    val field2 get() = initializer.result2
    val field3 get() = initializer.result3
}

6. 最佳实践建议

  1. 🎯 明确目的:只在真正需要委托模式的地方使用它

  2. 📝 文档化:为自定义委托属性添加清晰的文档

  3. 🔍 可测试性:确保委托类的行为可以被单元测试覆盖

  4. 🛠 保持简单:不要过度使用委托,导致代码难以理解

  5. 💡 性能考虑:在性能敏感的场景下,评估委托带来的开销

通过这些生动的例子和实践建议,相信您对 Kotlin 的委托机制已经有了更深入的理解。委托机制不仅让代码更加优雅,还能帮助我们更好地实现各种设计模式和功能需求。记住,委托就像现实生活中的"代办",合理使用可以让代码更加清晰和可维护。