Kotlin 从入门到进阶 之委托 模块(六)

Kotlin 委托模块

1. 属性委托基础概念

kotlin 复制代码
// 委托语法:by 关键字
// 语法:val/var <属性名>: <类型> by <委托对象>

class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "Value from delegate"
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("Setting value: $value")
    }
}

class MyClass {
    val prop: String by Delegate()
}

val obj = MyClass()
println(obj.prop)  // "Value from delegate"

委托是如何工作的

kotlin 复制代码
// Kotlin 编译器生成的代码大致等价于:
class MyClass {
    private val delegate = Delegate()

    val prop: String
        get() = delegate.getValue(this, ::prop)
}

// 可通过 :: 反射获取属性元数据
val name = MyClass::class.java.getDeclaredField("prop")
val delegateField = name.getAnnotation(Detail::class.java)

ReadOnly 和 ReadWrite 委托接口

kotlin 复制代码
// ReadOnlyProperty:val 使用
interface ReadOnlyProperty<in T, out V> {
    operator fun getValue(thisRef: T, property: KProperty<*>): V
}

// ReadWriteProperty:var 使用
interface ReadWriteProperty<in T, V> {
    operator fun getValue(thisRef: T, property: KProperty<*>): V
    operator fun setValue(thisRef: T, property: KProperty<*>, value: V)
}

2. 系统内置委托

by lazy 懒加载

kotlin 复制代码
// 懒加载:首次访问时执行初始化,之后缓存结果
class HeavyClass {
    init {
        println("HeavyClass initialized")
    }
}

class MyService {
    // 方式一:默认 lazy(LazilyInitializedSynchronized)
    val heavy: HeavyClass by lazy {
        HeavyClass()
    }

    // 方式二:LazyThreadSafetyMode.NONE(非线程安全,不推荐)
    val unsafeHeavy: HeavyClass by lazy(LazyThreadSafetyMode.NONE) {
        HeavyClass()
    }

    // 方式三:LazyThreadSafetyMode.PUBLICATION(多线程竞争时用发布式初始化)
    val heavy2: HeavyClass by lazy(LazyThreadSafetyMode.PUBLICATION) {
        HeavyClass()
    }
}

// 验证懒加载行为
val service = MyService()
println("Before access")           // 输出
service.heavy                      // 首次访问,触发初始化
println("After first access")     // 输出
service.heavy                      // 不再初始化,直接返回缓存

// 输出顺序:
// Before access
// HeavyClass initialized
// After first access
// (第二次不输出 "HeavyClass initialized")

by lazy 注意事项

kotlin 复制代码
// 只能用于 val(不可变),var 需要其他方案
// class Foo {
//     var value: String by lazy { "computed" }  // 编译错误
// }

// var 的惰性方案:lateinit var + 手动判空
class Service {
    lateinit var config: Config
    val resolved: Config
        get() = config  // 强制解包
}

// by lazy 不可用于构造函数参数(初始化顺序问题)

Delegates.observable 可观察

kotlin 复制代码
// observable:属性值变化时通知监听器
import kotlin.properties.Delegates

class Counter {
    var count: Int by Delegates.observable(0) { property, oldValue, newValue ->
        println("${property.name}: $oldValue -> $newValue")
    }
}

val counter = Counter()
counter.count++   // count: 0 -> 1
counter.count += 5  // count: 1 -> 6

// 输出:
// count: 0 -> 1
// count: 1 -> 6

observable vs vetoable

kotlin 复制代码
// observable:值变化后回调
var value: String by Delegates.observable("initial") { _, old, new ->
    println("Changed: $old -> $new")
}

// vetoable:值变化前回调,可拒绝修改
var positive: Int by Delegates.vetoable(0) { _, old, new ->
    println("Attempting: $old -> $new")
    new > 0  // 返回 true 才接受修改
}

positive = 5   // 打印 Attempting: 0 -> 5,接受,返回 true
positive = -1  // 打印 Attempting: 5 -> -1,拒绝,保持 5
positive       // 5

Delegates.notNull 非空延迟赋值

kotlin 复制代码
// notNull:var 延迟赋值,访问时必须已赋值
class Config {
    var endpoint: String by Delegates.notNull<String>()

    fun initialize(url: String) {
        endpoint = url  // 延迟到此处赋值
    }
}

val config = Config()
// config.endpoint  // 抛异常:Not initialized

config.initialize("https://api.example.com")
config.endpoint    // "https://api.example.com"

notNull vs lateinit

特性 lateinit var Delegates.notNull<T>
支持类型 任意非空类型 任意可空类型
首次访问 不检查,直接解包 未初始化则抛异常
primitive 类型 支持 支持
校验 依赖开发者保证 依赖运行时校验
Android ⚠️ 可能因配置重建失效 同样可能失效
kotlin 复制代码
// notNull 适合:需要在初始化后才能确定值的 var
class UserManager {
    var currentUser: User? by Delegates.notNull<User?>()
    // lateinit var currentUser: User  // 不支持可空类型

    fun login(user: User) {
        currentUser = user
    }

    fun getUser(): User {
        return currentUser  // 不需要 !!,已保证非空
    }
}

3. 类委托(接口代理、装饰器模式)

接口代理

kotlin 复制代码
// 接口代理:实现接口的任务委托给另一个对象
interface Animal {
    fun sound(): String
    fun move(): String
}

// 委托实现
class Dog : Animal {
    override fun sound() = "Woof!"
    override fun move() = "Running"
}

// Robot 代理 Animal 接口
class Robot(dog: Dog) : Animal by dog {
    // 自动生成转发代码,等价于:
    // override fun sound() = dog.sound()
    // override fun move() = dog.move()
}

val robot = Robot(Dog())
robot.sound()  // "Woof!"
robot.move()   // "Running"

多接口代理

kotlin 复制代码
interface Walkable {
    fun walk(): String
}

interface Speakable {
    fun speak(): String
}

class Human : Walkable, Speakable {
    override fun walk() = "Walking"
    override fun speak() = "Speaking"
}

// Robot 代理多个接口
class CompanionRobot(
    val walkable: Walkable,
    val speakable: Speakable
) : Walkable by walkable, Speakable by speakable

val robot = CompanionRobot(Human(), Human())
robot.walk()   // "Walking"
robot.speak()  // "Speaking"

装饰器模式(增强功能)

kotlin 复制代码
// 基础实现
class DataStore {
    private val data = mutableMapOf<String, Any>()

    fun put(key: String, value: Any) {
        data[key] = value
    }

    fun get(key: String): Any? = data[key]
}

// 装饰器:添加缓存
class CachedDataStore(
    private val delegate: DataStore
) : DataStore by delegate {
    private val cache = mutableMapOf<String, Any>()

    override fun get(key: String): Any? {
        return cache.getOrPut(key) {
            delegate.get(key) ?: "null from cache"
        }
    }

    override fun put(key: String, value: Any) {
        delegate.put(key, value)
        cache[key] = value  // 同时更新缓存
    }
}

// 装饰器:添加日志
class LoggingDataStore(
    private val delegate: DataStore
) : DataStore by delegate {
    override fun put(key: String, value: Any) {
        println("PUT: $key = $value")
        delegate.put(key, value)
    }

    override fun get(key: String): Any? {
        println("GET: $key")
        return delegate.get(key)
    }
}

// 组合装饰器
val store = LoggingDataStore(CachedDataStore(DataStore()))
store.put("name", "windCloud")
store.get("name")  // 打印日志,同时命中缓存

类委托注意事项

kotlin 复制代码
// 类委托不支持多态:delegate 静态类型
interface Base {
    fun hello(): String
}

class Real : Base {
    override fun hello() = "Real"
}

class Fake : Base {
    override fun hello() = "Fake"
}

class Wrapper(real: Real) : Base by real {
    // delegate 静态类型是 Real
    // 即使传入 Fake,也无法调用 Fake 的方法
}

val wrapper = Wrapper(Fake())  // ⚠️ 编译警告,类型不匹配
wrapper.hello()  // "Fake" - 但这是运行时多态,不是委托

4. 自定义属性委托

基础模板

kotlin 复制代码
import kotlin.properties.ReadWriteProperty
import kotlin.properties.ReadOnlyProperty

// 自定义 ReadWriteProperty 模板
class MyDelegate<T> : ReadWriteProperty<Any?, T> {
    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        println("Getting ${property.name}")
        return /* value */
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        println("Setting ${property.name} = $value")
        /* save value */
    }
}

SharedPreferences 委托(Android 常用)

kotlin 复制代码
import android.content.Context
import android.content.SharedPreferences
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

class Preference<T>(
    private val context: Context,
    private val key: String,
    private val default: T
) : ReadWriteProperty<Any?, T> {

    private val prefs: SharedPreferences by lazy {
        context.getSharedPreferences("default", Context.MODE_PRIVATE)
    }

    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return findPreference(key, default)
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        putPreference(key, value)
    }

    @Suppress("UNCHECKED_CAST")
    private fun findPreference(key: String, default: T): T {
        return when (default) {
            is String -> prefs.getString(key, default) as T
            is Int -> prefs.getInt(key, default) as T
            is Long -> prefs.getLong(key, default) as T
            is Float -> prefs.getFloat(key, default) as T
            is Boolean -> prefs.getBoolean(key, default) as T
            is Set<*> -> prefs.getStringSet(key, default as Set<String>) as T
            else -> throw UnsupportedOperationException("Unsupported type")
        }
    }

    private fun putPreference(key: String, value: T) {
        prefs.edit().apply {
            when (value) {
                is String -> putString(key, value)
                is Int -> putInt(key, value)
                is Long -> putLong(key, value)
                is Float -> putFloat(key, value)
                is Boolean -> putBoolean(key, value)
                is Set<*> -> putStringSet(key, value as Set<String>)
            }
        }.apply()
    }
}

// 使用方式
class UserPreferences(context: Context) {
    var userName: String by Preference(context, "user_name", "")
    var userAge: Int by Preference(context, "user_age", 0)
    var isLogin: Boolean by Preference(context, "is_login", false)
    var tags: Set<String> by Preference(context, "tags", emptySet())
}

// 调用
val prefs = UserPreferences(context)
prefs.userName = "windCloud"
prefs.isLogin = true
println(prefs.userName)  // "windCloud"

加密 SharedPreferences 委托

kotlin 复制代码
import android.content.Context
import android.content.SharedPreferences
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

class EncryptedPreference<T>(
    context: Context,
    private val key: String,
    private val default: T
) : ReadWriteProperty<Any?, T> {

    private val prefs: SharedPreferences by lazy {
        val masterKey = MasterKey.Builder(context)
            .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
            .build()

        EncryptedSharedPreferences.create(
            context,
            "secure_prefs",
            masterKey,
            EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
            EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
        )
    }

    // 实现同上,类型处理省略
    // 完整实现参考上面的 Preference 类
}

// 使用
class SecurePrefs(context: Context) {
    var apiToken: String by EncryptedPreference(context, "api_token", "")
    var refreshToken: String by EncryptedPreference(context, "refresh_token", "")
}

StateFlow 状态委托(Jetpack Compose 常用)

kotlin 复制代码
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

class StateDelegate<T>(initial: T) : ReadWriteProperty<Any?, T> {
    private val flow = MutableStateFlow(initial)

    override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return flow.value
    }

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        flow.value = value
    }
}

// ViewModel 中使用
class MyViewModel : ViewModel() {
    var name: String by StateDelegate("")
    var count: Int by StateDelegate(0)
    var isLoading: Boolean by StateDelegate(false)
}

// Compose 中收集
@Composable
fun Screen(viewModel: MyViewModel = viewModel()) {
    val name by viewModel.name.collectAsState()
    // 每次 viewModel.name 变化,Compose 自动重组
}

// 注意:StateDelegate 适合简单场景,复杂场景直接用 MutableStateFlow

运行时属性验证委托

kotlin 复制代码
class ValidatedDelegate<T : Any>(
    private val default: T,
    private val validator: (T) -> Boolean
) : ReadWriteProperty<Any?, T> {

    private var value: T = default

    override fun getValue(thisRef: Any?, property: KProperty<*>): T = value

    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        require(validator(value)) {
            "Validation failed for ${property.name}: $value"
        }
        this.value = value
    }
}

// 邮箱验证
class UserInput {
    var email: String by ValidatedDelegate(
        default = "",
        validator = { it.isEmpty() || it.contains("@") }
    )

    var age: Int by ValidatedDelegate(
        default = 0,
        validator = { it in 0..150 }
    )
}

val input = UserInput()
input.email = "valid@email.com"  // OK
// input.email = "invalid"        // 抛异常

惰性委托工厂

kotlin 复制代码
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

// 带名称前缀的委托工厂
class Prefs(
    private val prefix: String,
    private val context: Context
) : ReadOnlyProperty<Any?, String> {

    private var cached: String? = null

    override fun getValue(thisRef: Any?, property: KProperty<*>): String {
        if (cached == null) {
            cached = context.getSharedPreferences("app", Context.MODE_PRIVATE)
                .getString("$prefix.${property.name}", "") ?: ""
        }
        return cached!!
    }
}

// 使用
class PreferencesRepository(private val context: Context) {
    val userName: String by Prefs("user", context)
    val userEmail: String by Prefs("user", context)
    val appTheme: String by Prefs("app", context)
}

// 生成代码等效于:
// private val userName$delegate = Prefs("user", context)
// val userName: String get() = userName$delegate.getValue(this, ::userName)

5. 委托综合实战

ViewModel 状态委托完整示例

kotlin 复制代码
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlin.properties.ReadOnlyProperty
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

// 带 StateFlow 的只读属性委托
class StateFlowDelegate<T>(private val flow: MutableStateFlow<T>) :
    ReadOnlyProperty<Any?, T> {
    override fun getValue(thisRef: Any?, property: KProperty<*>) = flow.value
}

// ViewModel 完整封装
class MyViewModel(
    private val repository: MyRepository
) : ViewModel() {

    // UI 状态
    private val _uiState = MutableStateFlow<UiState>(UiState.Idle)
    val uiState: StateFlow<UiState> = _uiState.asStateFlow()

    // 数据列表
    private val _items = MutableStateFlow<List<Item>>(emptyList())
    val items: StateFlow<List<Item>> = _items.asStateFlow()

    // 加载状态(简化访问)
    var isLoading: Boolean by object : ReadWriteProperty<Any?, Boolean> {
        override fun getValue(thisRef: Any?, property: KProperty<*>) = _uiState.value is UiState.Loading
        override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) {
            _uiState.value = if (value) UiState.Loading else UiState.Idle
        }
    }

    fun loadData() {
        viewModelScope.launch {
            _uiState.value = UiState.Loading
            try {
                val data = repository.fetchData()
                _items.value = data
                _uiState.value = UiState.Success
            } catch (e: Exception) {
                _uiState.value = UiState.Error(e.message ?: "Unknown error")
            }
        }
    }
}

快速对照表

委托类型 语法 用途
by lazy val x by lazy { ... } 惰性初始化,线程安全
Delegates.observable var x by Delegates.observable(init) { ... } 值变化监听
Delegates.vetoable var x by Delegates.vetoable(init) { ... } 值变化前校验
Delegates.notNull var x by Delegates.notNull<T>() var 延迟非空赋值
类委托 class Foo : Bar by delegate 接口方法代理
自定义委托 class MyDelegate<T> : ReadWriteProperty 自定义读写逻辑

Android 推荐委托

  • SharedPreferencesPreference<T> 委托
  • StateFlowStateDelegate<T> 委托
  • sealed class State → 配合 observable 监听状态变化

记忆口诀

  • 延迟初始化 → by lazy
  • 监听值变化 → observable
  • var 延迟赋值 → notNull
  • 接口方法代理 → by 类委托
  • 自定义存储 → 实现 ReadWriteProperty
相关推荐
zhoutongsheng1 小时前
CSS如何使用-hover显示图片文字说明_利用--after实现图文叠加效果
jvm·数据库·python
2301_783848651 小时前
CSS解决浮动元素导致的布局闪烁_稳定容器布局高度
jvm·数据库·python
m0_740796361 小时前
Workerman5.0协程实战:PHP高并发新标准
jvm·数据库·python
2301_769340671 小时前
如何在 CSS 中实现元素的绝对定位,使其不受窗口尺寸变化影响
jvm·数据库·python
m0_702036531 小时前
防止SQL注入的运维实践_实时清理数据库缓存与历史记录
jvm·数据库·python
05候补工程师1 小时前
[架构思维] 拒绝面条代码!我用一套“基石指令”调教 AI 撸出了 408 抽测系统
python·考研·系统架构·ai编程
2301_779622411 小时前
Redis怎样合并多天访客数据_通过PFMERGE指令聚合HyperLogLog记录
jvm·数据库·python
m0_748554811 小时前
如何监控集群 interconnect_ping与traceroute验证心跳通畅
jvm·数据库·python
奔跑的蜗牛FelixChioa1 小时前
python异常处理机制详解
开发语言·python