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 推荐委托:
SharedPreferences→Preference<T>委托StateFlow→StateDelegate<T>委托sealed class State→ 配合observable监听状态变化
记忆口诀:
- 延迟初始化 →
by lazy - 监听值变化 →
observable - var 延迟赋值 →
notNull - 接口方法代理 →
by类委托 - 自定义存储 → 实现
ReadWriteProperty