专做宝宝的用品网站/seo综合查询软件排名
一、概念
- 类委托:将类需要重写的方法委托给另一个对象来实现,以减少模板代码。
- 属性委托:将属性的访问器委托给另一个对象,以减少模板代码并隐藏访问细节,有三种方式实现:
- 重载运算符:重写getValue()、setValue()。
- 实现接口:使用系统提供的Delegates、或者自己实现接口。
- 扩展方法:像lazy那样通过扩展函数重载运算符。
- Map委托:直接委托给一个Map实例。
二、类委托
把原本自己做的事情委托给另一个对象去做。装饰者模式和代理模式都通过委托复用了行为,Kotlin 在语言层面支持了委托。
interface Function {fun run(): Stringfun jump(): String
}class Model1 : Function {override fun run(): String = "run"override fun jump(): String = "jump"
}
//装饰者模式
//重写时调用持有的实例去做
class Model2(val function: Function) : Function { //持有一个实例override fun run() = function.run() + " fast" //对实例加强override fun jump() = function.jump() //委托实例做
}
//Kotlin委托
//实现Function接口需要的重写,委托给持有的实例去做
class Model3(val function: Function) : Function by function{//加强的功能还是需要自己重写//完全委托的功能可以不写,减少了模版代码
}val model3 = Model3(Model1())
model3.run()
三、属性委托
3.1 重载运算符
by约定:将属性的 getter/setter 交给别的对象重载运算符 getValue()/setValue() 去实现。也就是被 var 属性委托的对象的类要重写 getValue()、setValue()方法,被 val 属性委托的对象的类要重写 getValue() 方法。
//thisRef:属性所在类的类型
//property:属性
//value:属性的类型
operator fun getValue(thisRef: Any?, property: KProperty<*>)
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String)
class Phone {//一般是先委托再去被委托的类中重写,这样IDE能自动补全代码,手写也好去写类型val screen: String by ScreenFactory() //val属性委托给屏幕对象var battery: String by BatteryFactory() //var属性委托给电池对象
}//被委托的屏幕类
class ScreenFactory {private val str: String = "屏幕"operator fun getValue(phone: Phone, property: KProperty<*>): String = str
}//被委托的电池类
class BatteryFactory {private var str = "电池"operator fun getValue(phone: Phone, property: KProperty<*>): String = stroperator fun setValue(phone: Phone, property: KProperty<*>, s: String) { str = s }
}val phone = Phone()
println(phone.battery) //打印:电池
phone.battery = "坏电池"
println(phone.battery) //打印:坏电池
3.2 实现接口 Delegates
结构 | 说明 | 重点关注的地方 |
ReadOnlyProperty | val 属性代理对象的通用接口。 | getValue( ) 用来代理属性的getter |
ReadWriteProperty | var 属性代理对象的通用接口。 | setValue( ) 增加用来代理属性的setter |
NotNullVar | notNull( ) 返回的代理对象的类。 | 空判断 |
ObserableProperty | observable( ) 返回的代理对象的类。 | afterChange( ) 赋值前判断是否允许 |
vetoable( ) 返回的代理对象的类。 | beforeChange( ) 赋值后提供数据反馈 | |
Degelates | 是一个单例对象,通过不同方法返回不同的代理对象。 | notNull( ) 返回一个NotNullVar对象 observable( ) 返回一个ObserableProperty对象 vetoable( ) 返回一个ObserableProperty对象 |
3.2.1 Delegates.notNull( )
可以不在属性声明的时候初始化,而是延迟到类中其他地方再初始化。属性委托后,编译器不会再做非空检查,不初始化也不会报错,所以需要做到自己可控(无论在类中还是类外要对属性赋值),否则在使用之前未初始化,调用会报错 IllegalStateException。
Delegates.notNull() | 适用于:基本类型、引用类型。 不支持外部注入工具将它直接注入到Java字段中。 |
lateinit | 适用于:引用类型。 |
3.2.2 Delegates.observable( )
类似于观察者用于监听属性值发生变更,当属性值被更改后会往外抛出一个回调。
3.2.3 Delegates.vetoable( )
同上,不同的是回调会返回一个 Boolean 值,来决定此次修改是否通过。
class Demo {var num1: Int by Delegates.notNull()var num2: Int by Delegates.observable(1) { property, oldValue, newValue -> println("发生了变化:${property.name},$oldValue,$newValue") }//新值是负数才能修改成功var num3: Int by Delegates.vetoable(1) { property, oldValue, newValue -> newValue < 0 }
}val demo = Demo()
demo.num1 = 3
println(demo.num1) //打印:3
demo.num2 = 3
println(demo.num3) //打印:发生了变化:num2,1,3 1
demo.num3 = 3
println(demo.num3) //打印:1
demo.num3 = -3
println(demo.num3) //打印:-3
使用场景①:更方便获取 map 值。Kotlin 标准库已经为 Map 定义了getValue()
和setValue()
扩展函数。属性名将自动作用于 map 的键。
class Demo {val attrs = mapOf<String, Int>()val name: String by attrs
}
使用场景②:Activity的findViewById。委托可以隐藏细节,特别是一些模板代码的时候.。
//被委托的类
class Find {operator fun getValue(activity: Activity, kProperty: KProperty<*>): T {println("操作的属性名是 ${property.name}")return activity.findViewById(id)}
}
//给Activity扩展
fun <T : View> Activity.find(id: Int) = Find<T>(id)
//使用
class KotlinActivity : AppCompatActivity() {private val image: ImageView by find(R.id.iv)override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)}
}
2.3 扩展方法 by lazy( )
只有 by 才是关键字,lazy()是高阶函数。在 lazy() 中会创建并返回一个 Delegate对象,当我们调用属性的时候其实是调用 Delegate对象的 getValue()函数,getValue()中执行了我们传入的Lambda。
- 延迟对象的初始化,直到第一次访问(使用)它,避免类加载的时候过多消耗资源。
- 只能对 val 的属性使用。第一次调用会执行传入的 Lambda 并记录和返回结果,再次调用只返回之前记录的结果。
- 线程安全。
Lazy.kt | Lazy 接口 | public interface Lazy<out T> { public val value: T //懒加载的值,一旦被赋值件将不会被改变 public fun isInitialized(): Boolean //检查是否被初始化 } |
by 关键字 | public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value | |
by的实现就是把属性的getter和被委托对象的类中重写的getValue()配对,这里是Lazy的扩展函数形式。 | ||
LazyJVM.kt | lazy() 函数 | public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer) |
lazy()参数接收一个Lambda返回一个Lazy的实现类实例,有很多重载一般使用上面这个线程同步的,原理双重校验锁。 |
使用场景:所有的员工只有柜台是女性,针对少数使用到的情况选择懒加载。
//这样写在实例初始化的时候就会初始化 gender 这个属性。
class Staff(val name: String, var position: String) {val gender: String = if (position == "Counter") "female" else "male"
}
//修改之后没有了= 赋值,只有在初次访问sex这个属性的时候,才会进行初始化。
class Staff(val name: String, var position: String) {val gender: String by lazy {if (position == "Counter") "female" else "male"}
}
2.4 Map委托
- 属性名要和 Map 的 key 一致。
- val可以委托给 Map 或 MutableMap,var只能委托给MutableMap。
class Person(mutableMap: MutableMap<String, Any?>) {val id: Int by mutableMapvar name: String by mutableMap
}fun main() {val mutableMap = mutableMapOf<String,Any?>("id" to 123456,"name" to "ZhangSan")val person = Person(mutableMap)println(person.id) //打印:123456println(person.name) //打印:ZhangSan
// person.id = 654231 //对val属性再次赋值会报错person.name = "LiSi"println(person.name) //打印:LiSi
}