一、委托(Delegation)
1. 类委托
类委托允许将接口的实现委托给另一个对象,避免继承的局限性,适用于组合模式。
示例代码:
interface Printer {
fun printMessage(message: String)
}
class ConsolePrinter : Printer {
override fun printMessage(message: String) {
println("控制台输出: $message")
}
}
// 通过委托将Printer接口的实现交给ConsolePrinter
class LogPrinter(printer: Printer) : Printer by printer {
override fun printMessage(message: String) {
println("日志记录开始")
printer.printMessage(message)
println("日志记录结束")
}
}
fun main() {
val consolePrinter = ConsolePrinter()
val logPrinter = LogPrinter(consolePrinter)
logPrinter.printMessage("测试消息")
}
使用场景:
- 需要增强或修改现有类的行为(装饰器模式)。
- 避免多层继承带来的复杂性。
2. 属性委托
属性委托将属性的 getter
/setter
逻辑委托给其他对象,Kotlin 标准库提供多种内置委托。
常用内置委托:
lazy
:延迟初始化属性。observable
:监听属性变化。vetoable
:在赋值前验证值。
示例代码:
import kotlin.properties.Delegates
class Example {
// 延迟初始化,首次访问时计算
val lazyValue: String by lazy {
println("计算lazyValue")
"Hello, Lazy!"
}
// 监听属性变化
var name: String by Delegates.observable("<未命名>") { _, old, new ->
println("名称从 $old 更改为 $new")
}
// 仅允许非负数值
var age: Int by Delegates.vetoable(0) { _, _, new ->
new >= 0
}
}
fun main() {
val example = Example()
println(example.lazyValue) // 输出:计算lazyValue \n Hello, Lazy!
example.name = "Kotlin" // 输出:名称从 <未命名> 更改为 Kotlin
example.age = -5 // 赋值失败,age保持0
println(example.age) // 输出:0
}
使用场景:
lazy
:初始化成本高的资源(如数据库连接)。observable
:实现数据绑定或响应式UI更新。vetoable
:表单输入验证。
3. 自定义属性委托
通过实现 ReadWriteProperty
或 ReadOnlyProperty
接口创建自定义委托。
示例代码:
import kotlin.reflect.KProperty
class FormatDelegate(private val format: String) : ReadWriteProperty<Any?, String> {
private var value: String = ""
override fun getValue(thisRef: Any?, property: KProperty<*>): String {
return value.format(format)
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
this.value = value
}
}
class User {
var username: String by FormatDelegate("用户名:%s")
}
fun main() {
val user = User()
user.username = "Alice"
println(user.username) // 输出:用户名:Alice
}
使用场景:
- 统一格式化字符串、数值等属性。
- 自动加密/解密敏感数据字段。
二、扩展函数(Extension Functions)
1. 基本用法
扩展函数允许为现有类添加新方法,无需继承或修改原始类。
示例代码:
// 为String添加判断是否为有效邮件的扩展函数
fun String.isValidEmail(): Boolean {
return matches(Regex("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"))
}
// 为Int添加计算阶乘的扩展函数
fun Int.factorial(): Long {
return if (this <= 1) 1 else this * (this - 1).factorial()
}
fun main() {
println("user@example.com".isValidEmail()) // 输出:true
println(5.factorial()) // 输出:120
}
使用场景:
- 为第三方库或系统类添加实用方法。
- 增强代码可读性,如
list.quickSort()
。
2. 扩展属性
类似扩展函数,可以为类添加“属性”(实际是计算属性,无幕后字段)。
示例代码:
val String.firstLetter: Char
get() = if (isEmpty()) ' ' else this[0]
fun main() {
println("Kotlin".firstLetter) // 输出:K
}
使用场景:
- 提供基于现有属性的快捷访问方式,如
file.extension
。
3. 扩展与成员冲突
当扩展函数与类成员函数同名时,成员函数优先。
示例代码:
class Example {
fun print() = println("成员函数")
}
fun Example.print() = println("扩展函数")
fun main() {
Example().print() // 输出:成员函数
}
三、注意事项
1. 委托的可见性:
- 委托对象需在类构造时初始化。
- 避免在委托中持有外部类的引用导致内存泄漏。
2. 扩展的限制:
- 无法访问类的私有成员。
- 扩展是静态解析的,不具备多态性。
3. 性能考量:
lazy
委托默认是线程安全的,若不需要同步可用lazy(LazyThreadSafetyMode.NONE)
提升性能。
四、总结
特性 | 适用场景 | 优势 |
---|---|---|
类委托 | 组合优于继承、装饰器模式 | 减少重复代码,提升灵活性 |
属性委托 | 懒加载、属性监听、验证逻辑 | 逻辑复用,代码简洁 |
扩展函数 | 增强现有类功能、第三方库适配 | 无侵入式扩展,提高可读性 |
扩展属性 | 提供快捷访问属性 | 简化复杂计算逻辑的调用 |
合理运用委托和扩展函数,能显著提升Kotlin代码的简洁性和可维护性,但需注意避免滥用导致结构混乱。