委托(Delegation) 是一种 设计模式,用于让一个对象将其某些功能委托给另一个对象,无需继承即可复用已有功能。在 Java 中,委托通常是手动实现的。例如,我们想让 MyHashSet 复用 HashSet 的功能,但又希望扩展一些新功能,比如计数 add() 方法被调用的次数,一般我们会这样实现:
import java.util.HashSet;
class MyHashSet<E> {
private final HashSet<E> delegate = new HashSet<>();
private int addCount = 0;
public boolean add(E element) {
addCount++;
return delegate.add(element);
}
public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return delegate.addAll(c);
}
// 新增添加 add() 计数方法
public int getAddCount() {
return addCount;
}
...
// 所有的基础集合操作都需要重写,因为不重写没法用
}这里我们新增了一个add()计数方法,可以看到其他方法都是调用 delegate 来实现的。但是我们为了 MyHashSet 可用,我们会重写非常多的基础方法。这就导致了两个缺点:
- 样板代码多:我们需要手动 编写所有
HashSet的方法(add()、addAll()等),即使只想修改部分功能。 - 不够灵活:如果
HashSet方法新增了,我们必须手动更新MyHashSet。
Kotlin 委托
Kotlin 通过 by 关键字 让 类委托 变得更加简洁。同时,Kotlin 提供了 类委托(Class Delegation) 和 属性委托(Property Delegation) 两种方式来简化代码。
类委托(Class Delegation)
类委托 允许一个类将 接口的实现 委托给另一个对象,而不是自己去实现所有的方法。Kotlin 通过 by** 关键字** 实现这一功能。比如,可以直接 将 HashSet 的实现委托 给另一个 HashSet 实例:
// 将MutableSet接口委托给delegate
class CountingHashSet<E>(private val delegate: HashSet<E> = HashSet()) : MutableSet<E> by delegate {
// 自定义计数器
var addCount = 0
// 私有 setter 方法
private set
// 重写add方法
override fun add(element: E): Boolean {
addCount++ // 每次添加元素时计数器+1
return delegate.add(element) // 委托给HashSet实际执行添加
}
override fun addAll(elements: Collection<E>): Boolean {
addCount += elements.size // 按添加元素数量增加计数器
return delegate.addAll(elements) // 委托给HashSet实际执行批量添加
}
}
fun main() {
val set = CountingHashSet<Int>()
set.addAll(listOf(1, 2, 3, 4))
println(set.addCount) // 输出: 4
}by delegate表示将Set接口的实现委托给delegate(即HashSet),而大部分基础集合操作(如remove,contains等)通过by关键字自动代理,直接由HashSet处理,无需重复实现。我们只需要关注我们需要重写的方法即可。
Kotlin 属性委托(Property Delegation)
在 Java 中,我们如果想要延迟初始化一个变量,主要突出一个**“按需加载”**,通常使用 null 检查:
class LazyExample {
private String name;
public String getName() {
if (name == null) {
name = "Kotlin";
}
return name;
}
}by lazy
在 Kotlin 中,我们可以使用 **属性委托 **by lazy,让变量在第一次访问时 自动初始化:
class LazyExample {
// Kotlin 的 lazy 委托允许你在属性中直接编写初始化逻辑
val name: String by lazy {
println("初始化 name")
"Kotlin"
}
}
fun main() {
val example = LazyExample()
println("访问 name 之前")
println(example.name) // 触发 lazy 初始化
println(example.name) // 第二次访问,不会重新初始化
}运行结果
访问 name 之前
初始化 name
Kotlin
Kotlin这里 by lazy 并不是一个连在一起的关键字,只有by是关键字。
by lazy 工作原理解密
Kotlin 的 by lazy 本质上是 属性委托(Property Delegation),它的作用是 第一次访问属性时计算值并缓存,后续访问直接返回缓存值。
我们自己实现一个 myLazy,核心逻辑就是 延迟初始化 + 缓存值。
a. 非线程安全版 later
package 委托
import kotlin.reflect.KProperty
/**
* @author zhangzhen40
* @version
* @since 2025/03/17
*/
class Later<T>(private val block: () -> T) {
private var cachedValue: Any? = null
/**
* 当访问委托属性时,Kotlin 会自动调用 getValue 方法来获取属性的值
* 固定写法,Kotlin 委托属性的标准约定
*
* @param thisRed Any? 委托属性的接收者(即属性所属的对象)
* @param property KProperty<*> 委托属性的元信息(如属性名称、类型等)
* @return T
*/
operator fun getValue(thisRed: Any?, property: KProperty<*>): T {
if (cachedValue == null) {
println("\uD83D\uDD04 计算属性值: ${property.name}")
cachedValue = block()
}
return cachedValue as T
}
}
fun <T> later(block: () -> T) = Later(block)
// 使用自定义的 by lazy
class Example() {
val value: String by later {
println("🛠️ 正在初始化 value")
"Kotlin is awesome!"
}
}
fun main() {
val example = Example()
println("访问 value 之前")
println(example.value) // 第一次访问,计算并缓存
println(example.value) // 第二次访问,直接返回缓存值
}代码执行过程中发生了什么?
当创建 Example 的实例时,由于 value 是一个委托属性(使用了 by later),它的初始化被委托给了 Later 类的实例。Later 的构造函数接收一个 block() 参数,但这个 block 并不会立即执行。Later 的 getValue 方法中,只有在第一次访问 value 时,才会执行 block 并缓存结果。因此,value 的初始化被推迟到了第一次访问它的时候。
b. 线程安全版 later
class Later<T>(private val block: () -> T, private val strategy: LaterStrategy<T>) {
private var cachedValue: Any? = null
// 双重检验锁
operator fun getValue(thisRed: Any?, property: KProperty<*>): T {
return cachedValue as T ?: synchronized(this) {
cachedValue as T ?: block().also { cachedValue = it }
}
}
}c. 闲的没事干版 later
package 委托
import kotlin.reflect.KProperty
/**
* 策略+工厂实现一个根据健壮性的 Later
*
* @author zhangzhen40
* @version
* @since 2025/03/17
*/
/**
* 策略接口
*
* @param T
*/
interface LaterStrategy<T> {
operator fun getValue(thisRed: Any?, property: KProperty<*>): T
}
/**
* 非线程安全的 Later
*
* @param T
* @property block Function0<T>
* @property cachedValue Any?
* @constructor
*/
class Later<T>(private val block: () -> T) : LaterStrategy<T> {
private var cachedValue: Any? = null
/**
* 当访问委托属性时,Kotlin 会自动调用 getValue 方法来获取属性的值
* 固定写法,Kotlin 委托属性的标准约定
*
* @param thisRed Any? 委托属性的接收者(即属性所属的对象)
* @param property KProperty<*> 委托属性的元信息(如属性名称、类型等)
* @return T
*/
override operator fun getValue(thisRed: Any?, property: KProperty<*>): T {
if (cachedValue == null) {
println("\uD83D\uDD04 计算属性值: ${property.name}")
cachedValue = block()
}
return cachedValue as T
}
}
/**
* 线程安全的 Later
*
* @param T
* @property block Function0<T>
* @property cachedValue Any?
* @constructor
*/
class SafeLater<T>(private val block: () -> T) : LaterStrategy<T> {
@Volatile
private var cachedValue: Any? = null
override operator fun getValue(thisRed: Any?, property: KProperty<*>): T {
return cachedValue as T ?: synchronized(this) {
cachedValue as T ?: block().also { cachedValue = it }
}
}
}
/**
* 新学的技巧,使用 sealed class 代替 enum
*/
sealed class LaterMode {
object Normal : LaterMode() // 非线程安全
object ThreadSafe : LaterMode() // 线程安全
class Custom<T>(val strategy: LaterStrategy<T>) : LaterMode() // 允许用户自定义策略
}
/**
* 懒加载工厂
*/
object LaterFactory {
fun <T> create(mode: LaterMode, block: () -> T): LaterStrategy<T> {
return when (mode) {
is LaterMode.Custom<*> -> mode.strategy as LaterStrategy<T> // 处理 Custom 模式
is LaterMode.Normal -> Later(block)
is LaterMode.ThreadSafe -> SafeLater(block)
}
}
}
/**
* 懒加载顶层函数
*
* @param block Function0<T>
* @param mode LaterMode
* @return Later<T>
*/
fun <T> later(mode: LaterMode = LaterMode.Normal, block: () -> T) = LaterFactory.create(mode, block)
// 使用自定义的 by lazy
class Example {
val value: String by later {
println("🛠️ 正在初始化 value")
"Kotlin is awesome!"
}
}
fun main() {
val example = Example()
println("访问 value 之前")
println(example.value) // 第一次访问,计算并缓存
println(example.value) // 第二次访问,直接返回缓存值
}Delegates.observable
在 Kotlin 中,Delegates.observable 是 属性委托(Property Delegation) 的一种,属于 kotlin.properties.Delegates,用于 监听属性变化。
当属性值发生变化时,它会触发回调 beforeChange -> afterChange,你可以在回调中执行 日志、UI 刷新、触发事件 等操作。
a. Delegates.observable:监听属性变化
import kotlin.properties.Delegates
class User {
// 使用 observable 委托
var name: String by Delegates.observable("") { prop, old, new ->
println("${prop.name} 从 $old 改为 $new") // 属性变化时的回调
}
}
fun main() {
val user = User()
user.name = "Alice" // 输出: name 从 改为 Alice
user.name = "Bob" // 输出: name 从 Alice 改为 Bob
}name 的初始值是空字符串 "",当 name 值发生变化时,会触发 observable 的属性回调。
运行结果
name 从 改为 Alice
name 从 Alice 改为 Bobb. Delegates.vetoable:允许拒绝赋值
observable 只是 监听变化,如果你想在 值修改前拦截它(比如不能为空),可以使用 Delegates.vetoable。假设你有一个 SafeStorage 类,希望确保 value 属性不为空。
import kotlin.properties.Delegates
class SafeStorage {
var value: String by Delegates.vetoable("") { _, old, new ->
if (new.isEmpty()) false else true // 拒绝空值
}
}
fun main() {
val storage = SafeStorage()
storage.value = "Hello" // 成功赋值
storage.value = "" // 被拒绝,保持原值"Hello"
}更新: 2025-08-07 09:51:22
原文: https://www.yuque.com/dongpozhouzi-mshe3/zhm85g/etdr7mnd5dghhvsc