Kotlin 委托(Delegation)详解
委托是 Kotlin 中一种强大的设计模式,它允许对象将部分职责委托给另一个对象来完成。Kotlin 通过 by 关键字原生支持委托模式。
一、委托的基本概念
1. 委托模式 vs 继承
// 继承:is-a 关系
open class Animal {
open fun makeSound() = "Some sound"
}
class Dog : Animal() {
override fun makeSound() = "Bark"
}
// 委托:has-a 关系
interface SoundMaker {
fun makeSound(): String
}
class DogSoundMaker : SoundMaker {
override fun makeSound() = "Bark"
}
class Dog(private val soundMaker: SoundMaker) : SoundMaker by soundMaker {
// Dog 委托给 soundMaker 来实现 makeSound()
}
fun main() {
val dog1 = Dog() // 继承
println(dog1.makeSound()) // Bark
val dog2 = Dog(DogSoundMaker()) // 委托
println(dog2.makeSound()) // Bark
}
二、类委托(Class Delegation)
1. 基本语法
interface Printer {
fun printMessage(message: String)
fun printError(error: String)
}
class ConsolePrinter : Printer {
override fun printMessage(message: String) {
println("INFO: $message")
}
override fun printError(error: String) {
println("ERROR: $error")
}
}
// 委托给 consolePrinter
class Logger(private val consolePrinter: Printer) : Printer by consolePrinter {
// 可以添加额外的方法
fun logWithTimestamp(message: String) {
val timestamp = System.currentTimeMillis()
printMessage("[$timestamp] $message")
}
// 也可以重写委托的方法
override fun printError(error: String) {
consolePrinter.printError("!!! $error !!!")
}
}
fun main() {
val consolePrinter = ConsolePrinter()
val logger = Logger(consolePrinter)
logger.printMessage("Hello") // 委托给 ConsolePrinter
// 输出: INFO: Hello
logger.logWithTimestamp("World")
// 输出: INFO: [1645678901234] World
logger.printError("Something went wrong")
// 输出: ERROR: !!! Something went wrong !!!
}
2. 多接口委托
interface Reader {
fun read(): String
}
interface Writer {
fun write(content: String)
}
class FileHandler : Reader, Writer {
override fun read(): String {
return "File content"
}
override fun write(content: String) {
println("Writing: $content")
}
}
// 委托给多个接口
class DocumentProcessor(
private val reader: Reader,
private val writer: Writer
) : Reader by reader, Writer by writer {
fun process() {
val content = read() // 委托给 reader
val processed = content.uppercase()
write(processed) // 委托给 writer
}
}
fun main() {
val handler = FileHandler()
val processor = DocumentProcessor(handler, handler)
processor.process()
// 输出: Writing: FILE CONTENT
}
3. 动态代理模式
import java.lang.reflect.Proxy
interface Service {
fun serve()
}
class RealService : Service {
override fun serve() {
println("Real service serving...")
}
}
// 动态代理
class ServiceProxy(private val realService: Service) : Service by realService {
override fun serve() {
println("Before serving...")
realService.serve()
println("After serving...")
}
}
// Kotlin 风格代理
class LoggingServiceProxy(private val target: Service) : Service by target {
override fun serve() {
println("[LOG] Service.serve() called at ${System.currentTimeMillis()}")
target.serve()
println("[LOG] Service.serve() completed")
}
}
fun main() {
val realService = RealService()
val proxy = ServiceProxy(realService)
proxy.serve()
// 输出:
// Before serving...
// Real service serving...
// After serving...
val loggingProxy = LoggingServiceProxy(realService)
loggingProxy.serve()
}
三、属性委托(Property Delegation)
1. 基本语法
import kotlin.reflect.KProperty
class Delegate {
private var storedValue: String = ""
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
println("Getting value of '${property.name}' = $storedValue")
return storedValue
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("Setting value of '${property.name}' from $storedValue to $value")
storedValue = value
}
}
class Example {
var p: String by Delegate() // 属性委托
}
fun main() {
val example = Example()
example.p = "Hello" // 调用 Delegate.setValue()
// 输出: Setting value of 'p' from to Hello
println(example.p) // 调用 Delegate.getValue()
// 输出: Getting value of 'p' = Hello
// Hello
}
2. 延迟属性(Lazy Properties)
class HeavyResource {
init {
println("HeavyResource initialized - expensive operation!")
Thread.sleep(1000) // 模拟耗时初始化
}
fun doWork() {
println("HeavyResource working...")
}
}
class ResourceManager {
// 延迟初始化:第一次访问时才创建
val heavyResource: HeavyResource by lazy {
println("Creating HeavyResource...")
HeavyResource()
}
// 线程安全的延迟初始化
val threadSafeResource: HeavyResource by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
HeavyResource()
}
// 可空延迟属性
val nullableResource: HeavyResource? by lazy {
if (System.currentTimeMillis() % 2 == 0L) {
HeavyResource()
} else {
null
}
}
// 带参数的延迟初始化
val configuredResource by lazy {
val config = loadConfig()
HeavyResource().apply {
// 基于配置初始化
}
}
private fun loadConfig() = "config"
}
fun main() {
val manager = ResourceManager()
println("ResourceManager created")
// 此时 HeavyResource 还没有被创建
println("\nFirst access:")
manager.heavyResource.doWork()
// 输出: Creating HeavyResource...
// HeavyResource initialized - expensive operation!
// HeavyResource working...
println("\nSecond access:")
manager.heavyResource.doWork()
// 输出: HeavyResource working...
// 注意:不会再次初始化!
}
3. 可观察属性(Observable Properties)
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("<no name>") {
property, oldValue, newValue ->
println("$oldValue -> $newValue")
}
var age: Int by Delegates.observable(0) { _, old, new ->
when {
new > old -> println("年龄增长: $old -> $new")
new < old -> println("年龄减小: $old -> $new")
else -> println("年龄未变: $old")
}
}
var email: String by Delegates.vetoable("") { _, old, new ->
// 否决:如果新邮箱不包含@,则拒绝修改
if (new.contains("@")) {
println("邮箱从 $old 更新为 $new")
true // 接受修改
} else {
println("拒绝无效邮箱: $new")
false // 拒绝修改
}
}
// 带自定义逻辑的可观察属性
var score: Int by Delegates.observable(0) { prop, old, new ->
println("分数变化: $old -> $new")
when {
new >= 90 -> println("优秀!")
new >= 60 -> println("及格")
else -> println("不及格")
}
}
}
fun main() {
val user = User()
user.name = "Alice"
// 输出: <no name> -> Alice
user.name = "Bob"
// 输出: Alice -> Bob
user.age = 25
// 输出: 年龄增长: 0 -> 25
user.age = 25
// 输出: 年龄未变: 25
user.email = "alice@example.com"
// 输出: 邮箱从 更新为 alice@example.com
user.email = "invalid-email"
// 输出: 拒绝无效邮箱: invalid-email
println("邮箱仍然是: ${user.email}")
// 输出: 邮箱仍然是: alice@example.com
user.score = 85
// 输出: 分数变化: 0 -> 85
// 及格
}
4. Map 属性委托
class Configuration(map: Map<String, Any?>) {
// 从 Map 中读取属性
val host: String by map
val port: Int by map
val timeout: Long by map
val sslEnabled: Boolean by map
val maxConnections: Int? by map // 可空属性
// MutableMap 用于可变属性
class MutableConfig(map: MutableMap<String, Any?>) {
var debugMode: Boolean by map
var logLevel: String by map
var cacheSize: Int by map
}
}
// JSON/YAML 配置示例
data class AppConfig(
val configMap: Map<String, Any?>
) {
val database: DatabaseConfig by configMap
val server: ServerConfig by configMap
val features: FeaturesConfig by configMap
}
// 嵌套配置
data class DatabaseConfig(val url: String, val user: String, val password: String)
data class ServerConfig(val port: Int, val host: String)
data class FeaturesConfig(val analytics: Boolean, val notifications: Boolean)
fun main() {
// 从 Map 创建配置
val configMap = mapOf(
"host" to "localhost",
"port" to 8080,
"timeout" to 30000L,
"sslEnabled" to true,
"maxConnections" to 100
)
val config = Configuration(configMap)
println("Host: ${config.host}") // localhost
println("Port: ${config.port}") // 8080
println("SSL: ${config.sslEnabled}") // true
// 可变配置
val mutableMap = mutableMapOf(
"debugMode" to false,
"logLevel" to "INFO",
"cacheSize" to 1024
)
val mutableConfig = Configuration.MutableConfig(mutableMap)
println("初始日志级别: ${mutableConfig.logLevel}") // INFO
mutableConfig.logLevel = "DEBUG"
println("修改后Map: $mutableMap")
// 输出: {debugMode=false, logLevel=DEBUG, cacheSize=1024}
}
5. 自定义属性委托
import kotlin.reflect.KProperty
import java.text.SimpleDateFormat
import java.util.*
// 1. 日期格式化委托
class DateDelegate(private val pattern: String = "yyyy-MM-dd") {
private val formatter = SimpleDateFormat(pattern)
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return formatter.format(Date())
}
// 只读属性不需要 setValue
}
// 2. 缓存委托
class CacheDelegate<T : Any>(
private val initializer: () -> T
) {
private var cachedValue: T? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return cachedValue ?: initializer().also { cachedValue = it }
}
fun clearCache() {
cachedValue = null
}
}
// 3. 验证委托
class ValidatedDelegate<T>(
private val initialValue: T,
private val validator: (T) -> Boolean
) {
private var value: T = initialValue
operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value
operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T) {
if (validator(newValue)) {
value = newValue
} else {
throw IllegalArgumentException("Invalid value for ${property.name}: $newValue")
}
}
}
// 4. 线程安全委托
class SynchronizedDelegate<T>(private var value: T) {
private val lock = Any()
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
synchronized(lock) {
return value
}
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T) {
synchronized(lock) {
value = newValue
}
}
}
// 使用示例
class UserProfile {
// 日期格式化
val currentDate: String by DateDelegate()
val timestamp: String by DateDelegate("yyyy-MM-dd HH:mm:ss")
// 缓存
val expensiveCalculation: String by CacheDelegate {
println("计算中...")
Thread.sleep(1000)
"计算结果: ${System.currentTimeMillis()}"
}
// 验证
var age: Int by ValidatedDelegate(0) { it in 0..150 }
var email: String by ValidatedDelegate("") { it.contains("@") }
// 线程安全
var balance: Double by SynchronizedDelegate(0.0)
}
fun main() {
val profile = UserProfile()
println("当前日期: ${profile.currentDate}")
println("时间戳: ${profile.timestamp}")
println("\n第一次访问缓存:")
println(profile.expensiveCalculation)
println("\n第二次访问缓存(使用缓存):")
println(profile.expensiveCalculation)
println("\n验证属性:")
profile.age = 25 // 正常
println("年龄: ${profile.age}")
try {
profile.age = 200 // 抛出异常
} catch (e: IllegalArgumentException) {
println(e.message) // Invalid value for age: 200
}
profile.email = "test@example.com" // 正常
println("邮箱: ${profile.email}")
}
四、标准库中的委托
1. Delegates 工具类
import kotlin.properties.Delegates
class Settings {
// 1. notNull: 非空但延迟初始化
var notNullValue: String by Delegates.notNull<String>()
// 2. observable: 可观察属性
var observableValue: Int by Delegates.observable(0) { _, old, new ->
println("值从 $old 变为 $new")
}
// 3. vetoable: 可否决属性
var vetoableValue: String by Delegates.vetoable("default") { _, old, new ->
new.length <= 10 // 只接受长度小于等于10的字符串
}
}
fun main() {
val settings = Settings()
// notNull 必须在使用前初始化
// println(settings.notNullValue) // 抛出 IllegalStateException
settings.notNullValue = "Hello"
println(settings.notNullValue) // Hello
settings.observableValue = 10 // 输出: 值从 0 变为 10
settings.observableValue = 20 // 输出: 值从 10 变为 20
settings.vetoableValue = "short"
println("vetoableValue: ${settings.vetoableValue}") // short
settings.vetoableValue = "this string is too long"
println("vetoableValue: ${settings.vetoableValue}") // 仍然是 short
}
2. 使用 provideDelegate 操作符
import kotlin.reflect.KProperty
import kotlin.properties.ReadOnlyProperty
class ResourceDelegate<T : Any>(
private val provider: () -> T
) : ReadOnlyProperty<Any?, T> {
private var value: T? = null
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
if (value == null) {
value = provider()
}
return value!!
}
}
class ResourceLoader<T : Any>(private val provider: () -> T) {
// provideDelegate 在属性委托创建时调用
operator fun provideDelegate(
thisRef: Any?,
prop: KProperty<*>
): ReadOnlyProperty<Any?, T> {
println("为属性 '${prop.name}' 创建委托")
// 可以在这里进行验证或初始化
if (prop.name.contains("secret")) {
throw IllegalArgumentException("属性名不能包含 'secret'")
}
return ResourceDelegate(provider)
}
}
class AppConfig {
val databaseUrl: String by ResourceLoader {
"jdbc:mysql://localhost:3306/mydb"
}
val apiKey: String by ResourceLoader {
System.getenv("API_KEY") ?: "default-key"
}
// val secretInfo: String by ResourceLoader { "secret" } // 抛出异常
}
fun main() {
val config = AppConfig()
println("数据库URL: ${config.databaseUrl}")
println("API密钥: ${config.apiKey}")
}
五、实际应用场景
1. Android View 属性委托
// Kotlin Android 扩展的替代方案
class ActivityDelegate {
// 视图绑定委托
inline fun <reified T : View> bindView(id: Int): ReadOnlyProperty<Activity, T> {
return object : ReadOnlyProperty<Activity, T> {
private var value: T? = null
override fun getValue(thisRef: Activity, property: KProperty<*>): T {
if (value == null) {
value = thisRef.findViewById(id)
}
return value!!
}
}
}
// 参数委托
inline fun <reified T> argument(key: String): ReadOnlyProperty<Fragment, T> {
return object : ReadOnlyProperty<Fragment, T> {
override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
return thisRef.arguments?.get(key) as? T
?: throw IllegalArgumentException("参数 $key 不存在或类型不匹配")
}
}
}
}
// 使用示例
class MainActivity : AppCompatActivity() {
private val delegate = ActivityDelegate()
// 视图绑定
private val textView: TextView by delegate.bindView(R.id.text_view)
private val button: Button by delegate.bindView(R.id.button)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textView.text = "Hello, Delegation!"
button.setOnClickListener {
// 处理点击
}
}
}
class UserFragment : Fragment() {
private val delegate = ActivityDelegate()
// 参数委托
private val userId: String by delegate.argument("userId")
private val userName: String by delegate.argument("userName")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
println("用户ID: $userId")
println("用户名: $userName")
}
}
2. 数据库访问委托
// Room 或 SQLDelight 的委托扩展
class DatabaseDelegate {
// 数据库查询委托
fun <T> cachedQuery(
query: () -> T
): ReadOnlyProperty<Any?, T> {
return object : ReadOnlyProperty<Any?, T> {
private var cachedValue: T? = null
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
if (cachedValue == null) {
cachedValue = query()
}
return cachedValue!!
}
fun invalidate() {
cachedValue = null
}
}
}
// 事务委托
fun <T> transaction(
database: Database,
body: () -> T
): ReadOnlyProperty<Any?, T> {
return object : ReadOnlyProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return database.transaction {
body()
}
}
}
}
}
// 使用示例
class UserRepository(
private val userDao: UserDao,
private val database: Database
) {
private val delegate = DatabaseDelegate()
// 缓存用户查询
val allUsers: List<User> by delegate.cachedQuery {
userDao.getAll()
}
// 事务操作
val userCount: Int by delegate.transaction(database) {
userDao.getCount()
}
fun refreshUsers() {
// 使缓存失效
// 需要反射来访问委托的 invalidate 方法
// 或者使用不同的设计模式
}
}
3. 权限和安全性委托
import kotlin.reflect.KProperty
// 权限检查委托
class PermissionDelegate<T>(
private val permission: String,
private val defaultValue: T,
private val authorizedValue: T
) {
private val securityContext = SecurityContext()
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return if (securityContext.hasPermission(permission)) {
authorizedValue
} else {
defaultValue
}
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
if (securityContext.hasPermission(permission)) {
// 存储逻辑...
println("设置 ${property.name} = $value")
} else {
throw SecurityException("缺少权限: $permission")
}
}
}
// 加密属性委托
class EncryptedDelegate<T>(
private val encryptor: Encryptor,
private val initialValue: T
) {
private var encryptedValue: String = encryptor.encrypt(initialValue.toString())
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
val decrypted = encryptor.decrypt(encryptedValue)
return convertFromString(decrypted)
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
encryptedValue = encryptor.encrypt(value.toString())
}
@Suppress("UNCHECKED_CAST")
private fun convertFromString(value: String): T {
return when (initialValue) {
is String -> value as T
is Int -> value.toInt() as T
is Boolean -> value.toBoolean() as T
else -> throw IllegalArgumentException("不支持的类型")
}
}
}
// 使用示例
class SecureSettings {
// 权限控制
var adminSetting: String by PermissionDelegate(
"admin.settings",
defaultValue = "普通用户设置",
authorizedValue = "管理员设置"
)
// 加密存储
var apiKey: String by EncryptedDelegate(
encryptor = AesEncryptor(),
initialValue = ""
)
var secretNumber: Int by EncryptedDelegate(
encryptor = AesEncryptor(),
initialValue = 0
)
}
// 模拟安全上下文
class SecurityContext {
private val permissions = mutableSetOf("user.read", "user.write")
fun hasPermission(permission: String): Boolean {
return permission in permissions
}
fun grantPermission(permission: String) {
permissions.add(permission)
}
}
// 模拟加密器
interface Encryptor {
fun encrypt(text: String): String
fun decrypt(encrypted: String): String
}
class AesEncryptor : Encryptor {
override fun encrypt(text: String): String {
// 模拟加密
return "encrypted_${text.reversed()}"
}
override fun decrypt(encrypted: String): String {
// 模拟解密
return encrypted.removePrefix("encrypted_").reversed()
}
}
fun main() {
val settings = SecureSettings()
println("管理员设置: ${settings.adminSetting}")
// 输出: 管理员设置: 普通用户设置
// 授予权限
val context = SecurityContext()
context.grantPermission("admin.settings")
println("管理员设置: ${settings.adminSetting}")
// 输出: 管理员设置: 管理员设置
// 加密属性
settings.apiKey = "my-secret-api-key"
println("加密的API密钥: ${settings.apiKey}")
// 实际存储的是加密值
}
4. 缓存和性能优化
import kotlin.reflect.KProperty
import kotlin.system.measureTimeMillis
// 性能监控委托
class TimedDelegate<T>(private val delegate: () -> T) {
private var cachedValue: T? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
val time = measureTimeMillis {
if (cachedValue == null) {
cachedValue = delegate()
}
}
println("${property.name} 获取耗时: ${time}ms")
return cachedValue!!
}
fun clearCache() {
cachedValue = null
}
}
// 记忆化委托(Memoization)
class MemoizeDelegate<T, R>(private val function: (T) -> R) {
private val cache = mutableMapOf<T, R>()
operator fun getValue(thisRef: Any?, property: KProperty<*>): (T) -> R {
return { param: T ->
cache.getOrPut(param) {
function(param)
}
}
}
fun clearCache() {
cache.clear()
}
}
// 使用示例
class MathOperations {
// 带计时的延迟计算
val heavyCalculation: String by TimedDelegate {
Thread.sleep(1000) // 模拟耗时计算
"计算结果"
}
// 记忆化函数
val factorial: (Int) -> Long by MemoizeDelegate { n ->
if (n <= 1) 1L else n * factorial(n - 1)
}
val fibonacci: (Int) -> Long by MemoizeDelegate { n ->
when (n) {
0 -> 0L
1 -> 1L
else -> fibonacci(n - 1) + fibonacci(n - 2)
}
}
}
fun main() {
val math = MathOperations()
println("第一次获取:")
println(math.heavyCalculation) // 输出耗时信息
println("\n第二次获取(使用缓存):")
println(math.heavyCalculation) // 几乎没有耗时
println("\n计算阶乘:")
println("5! = ${math.factorial(5)}")
println("10! = ${math.factorial(10)}")
println("\n计算斐波那契数列:")
println("fib(10) = ${math.fibonacci(10)}")
println("fib(20) = ${math.fibonacci(20)}")
println("fib(30) = ${math.fibonacci(30)}") // 因为有记忆化,很快
}
六、委托的高级特性
1. 委托属性转换
import kotlin.reflect.KProperty
// 属性转换委托
class TransformDelegate<T, R>(
private val getter: (T) -> R,
private val setter: (R) -> T?
) {
private var rawValue: T? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): R {
return rawValue?.let { getter(it) }
?: throw IllegalStateException("属性未初始化")
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: R) {
rawValue = setter(value)
}
}
// 使用示例
class UserPreferences {
// 存储为毫秒,提供为日期字符串
var lastLogin: String by TransformDelegate(
getter = { millis ->
java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(Date(millis))
},
setter = { dateString ->
java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.parse(dateString)?.time
}
)
// 存储为字符串,提供为枚举
var theme: Theme by TransformDelegate(
getter = { string -> Theme.valueOf(string) },
setter = { enum -> enum.name }
)
}
enum class Theme { LIGHT, DARK, SYSTEM }
fun main() {
val prefs = UserPreferences()
prefs.lastLogin = "2024-01-15 14:30:00"
println("最后登录: ${prefs.lastLogin}")
prefs.theme = Theme.DARK
println("主题: ${prefs.theme}")
}
2. 委托属性组合
import kotlin.reflect.KProperty
import kotlin.properties.ReadOnlyProperty
import kotlin.properties.Delegates
// 组合多个委托
class CombinedDelegate<T>(
private vararg val delegates: ReadOnlyProperty<Any?, T>
) : ReadOnlyProperty<Any?, T> {
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
// 返回第一个非空委托的值
for (delegate in delegates) {
try {
return delegate.getValue(thisRef, property)
} catch (e: Exception) {
// 继续尝试下一个委托
}
}
throw IllegalStateException("所有委托都失败了")
}
}
// 使用示例
class FallbackConfiguration {
private val primaryConfig = mapOf("timeout" to 5000)
private val secondaryConfig = mapOf("timeout" to 3000)
private val defaultConfig = mapOf("timeout" to 1000)
val timeout: Int by CombinedDelegate(
// 尝试从主配置读取
object : ReadOnlyProperty<Any?, Int> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Int {
return primaryConfig[property.name] as? Int
?: throw NoSuchElementException()
}
},
// 尝试从备选配置读取
object : ReadOnlyProperty<Any?, Int> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Int {
return secondaryConfig[property.name] as? Int
?: throw NoSuchElementException()
}
},
// 使用默认值
object : ReadOnlyProperty<Any?, Int> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Int {
return defaultConfig[property.name] as Int
}
}
)
}
fun main() {
val config = FallbackConfiguration()
println("超时设置: ${config.timeout}ms") // 5000
// 即使 primaryConfig 缺少某个属性,也能正常工作
}
3. 委托属性的反射
import kotlin.reflect.KProperty
import kotlin.reflect.full.declaredMemberProperties
// 反射访问委托属性
class ReflectiveUser {
var name: String by Delegates.observable("") { _, old, new ->
println("名字从 '$old' 改为 '$new'")
}
var age: Int by Delegates.vetoable(0) { _, _, new ->
new >= 0
}
val metadata: Map<String, Any> by lazy {
mapOf("created" to System.currentTimeMillis())
}
}
// 委托属性分析器
class DelegateAnalyzer {
fun analyze(obj: Any) {
val kClass = obj::class
kClass.declaredMemberProperties.forEach { property ->
println("\n分析属性: ${property.name}")
println("类型: ${property.returnType}")
try {
val value = property.getter.call(obj)
println("值: $value")
// 检查是否是委托属性
if (property.name.startsWith("delegate")) {
println("这是一个委托属性")
}
} catch (e: Exception) {
println("获取值失败: ${e.message}")
}
}
}
}
fun main() {
val user = ReflectiveUser()
user.name = "Alice"
user.age = 25
val analyzer = DelegateAnalyzer()
analyzer.analyze(user)
}
七、最佳实践和注意事项
1. 性能考虑
class PerformanceConsiderations {
// ✅ 好的做法:对于计算成本高的属性使用 lazy
val expensiveResource by lazy {
HeavyResource()
}
// ❌ 不好的做法:对于简单属性过度使用委托
// val simpleValue by Delegates.notNull<String>() // 不必要的开销
// ✅ 好的做法:直接属性访问
val simpleValue: String = "直接值"
// ✅ 对于需要监听变化的属性使用 observable
var importantValue: String by Delegates.observable("") { _, old, new ->
onImportantValueChanged(old, new)
}
private fun onImportantValueChanged(old: String, new: String) {
// 处理变化
}
}
2. 线程安全
import java.util.concurrent.atomic.AtomicReference
import kotlin.properties.Delegates
class ThreadSafeDelegation {
// 使用同步委托
var sharedCounter: Int by Delegates.observable(0) { _, old, new ->
// observable 本身不是线程安全的
println("计数器: $old -> $new")
}
// 更好的线程安全方案
private val atomicCounter = AtomicReference(0)
val threadSafeCounter: Int
get() = atomicCounter.get()
fun incrementCounter() {
atomicCounter.updateAndGet { it + 1 }
}
// 自定义线程安全委托
class SynchronizedDelegate<T>(initialValue: T) {
@Volatile
private var value: T = initialValue
private val lock = Any()
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
return synchronized(lock) { value }
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T) {
synchronized(lock) {
value = newValue
}
}
}
var safeValue: String by SynchronizedDelegate("初始值")
}
3. 测试委托属性
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
class DelegatePropertyTest {
@Test
fun testLazyDelegate() {
var initializationCount = 0
val lazyValue by lazy {
initializationCount++
"初始化值"
}
assertEquals(0, initializationCount, "延迟属性不应立即初始化")
val value1 = lazyValue
assertEquals(1, initializationCount, "第一次访问应初始化")
assertEquals("初始化值", value1)
val value2 = lazyValue
assertEquals(1, initializationCount, "第二次访问不应重新初始化")
assertEquals("初始化值", value2)
}
@Test
fun testObservableDelegate() {
var lastChange: Pair<String, String>? = null
val observableValue by Delegates.observable("初始") { _, old, new ->
lastChange = old to new
}
// 触发变化
observableValue = "新值"
assertEquals("初始" to "新值", lastChange)
}
@Test
fun testVetoableDelegate() {
var changes = mutableListOf<String>()
val vetoableValue by Delegates.vetoable("初始") { _, old, new ->
val allowed = new.length <= 10
if (allowed) {
changes.add("$old -> $new")
}
allowed
}
vetoableValue = "短" // 允许
assertEquals("短", vetoableValue)
vetoableValue = "这个字符串太长了应该被拒绝" // 拒绝
assertEquals("短", vetoableValue, "过长的值应被拒绝")
assertEquals(listOf("初始 -> 短"), changes)
}
}
总结:
Kotlin 委托是一种强大的语言特性,主要分为:
-
类委托:
- 使用
by关键字将接口实现委托给另一个对象 - 减少样板代码,实现更好的组合而非继承
- 支持多个接口委托
- 使用
-
属性委托:
- Lazy:延迟初始化,首次访问时计算
- Observable:监听属性变化
- Vetoable:验证并可能否决属性修改
- Map 委托:从 Map 中读写属性
- 自定义委托:实现
getValue/setValue方法
-
实际应用:
- Android 视图绑定
- 配置管理
- 权限控制
- 缓存和性能优化
- 数据库访问
-
最佳实践:
- 合理使用委托,避免不必要的性能开销
- 考虑线程安全性
- 编写可测试的委托
- 使用标准委托优先于自定义委托
委托模式使 Kotlin 代码更加简洁、可维护和表达性强,是现代 Kotlin 开发中不可或缺的特性。