大家好,我是青空。今天给大家带来的是kotlin 委托。
文末可领取大厂面试题
Kotlin 直接支持委托模式,更加优雅,简洁
Kotlin 通过关键字 by 实现委托
所谓委托
就是在原来调用的对象上,包了一层
通过 包裹层调用 原来的对象
是 实现继承的一个很好的替代方法
类委托
这有3个部分
-
interface
-
interface的实现
-
委托类
-
外➕实际调用范例
用范例来看下 委托的3个部分
// 创建接口
interface Base {
fun print()
}
// 实现此接口的被委托的类
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
// 通过关键字 by 建立委托类
class Derived(b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // 输出 10
}
当然委托也支持重载
// 通过关键字 by 建立委托类
class Derived(b: Base) : Base by b
改成
// 通过关键字 by 建立委托类
class Derived(b: Base) : Base by b {
override fun print() { print("abc") }
fun print2() {println("the result of print2")}
}
输出就变成了abc
属性委托
val/var <属性名>: <类型> by <表达式>
不必实现任何接口,
但必须提供 getValue() 函数(对于 var属性,还需要 setValue() 函数)
定义一个被委托的类
范例
import kotlin.reflect.KProperty
// 定义包含属性委托的类
class Example {
var p: String by Delegate()
}
// 委托的类
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, 这里委托了 ${property.name} 属性"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$thisRef 的 ${property.name} 属性赋值为 $value")
}
}
fun main(args: Array<String>) {
val e = Example()
println(e.p) // 访问该属性,调用 getValue() 函数
e.p = "Runoob" // 调用 setValue() 函数
println(e.p)
}
标准委托
延迟属性 Lazy
范例
val lazyValue: String by lazy {
println("computed!") // 第一次调用输出,第二次调用不执行
"Hello"
}
fun main(args: Array<String>) {
println(lazyValue) // 第一次执行,执行两次输出表达式
println(lazyValue) // 第二次执行,只输出返回值
}
输出
computed!
Hello
Hello
-
lazy是系统提供的函数
-
提供了同步锁,是线程安全的
-
突然觉得 各种延迟初始化,变得很清晰,实现还很方便
可观察属性 Observable
-
observable 可以用于实现观察者模式
-
Delegates.observable() 函数接受两个参数
-
第一个是初始化值
-
第二个是属性值变化事件的响应器(handler)
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("初始值") {
prop, old, new ->
println("旧值:$old -> 新值:$new")
}
}
fun main(args: Array<String>) {
val user = User()
user.name = "第一次赋值"
user.name = "第二次赋值"
}
把属性储存在映射中
看《Kotlin入门-属性》
name\url是需要初始值的,否则会编译出错
通过将name、url交给map自身处理
- 达到延迟初始化的效果
达到了动态的效果
复习一下
val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))
println(user.name) // Prints "John Doe"
println(user.age) // Prints 25
Not Null
适用于那些无法在初始化阶段就确定属性值的场合
class Foo {
var notNullBar: String by Delegates.notNull<String>()
}
foo.notNullBar = "bar"
println(foo.notNullBar)
注意
- 不能在赋值前,被访问
局部委托属性
局部变量的延迟初始化
fun example(computeFoo: () -> Foo) {
val memoizedFoo by lazy(computeFoo)
if (someCondition && memoizedFoo.isValid()) {
memoizedFoo.doSomething()
}
}
首先定义了 by lazy(computeFoo)
注意
-
这里只会进行一次初始化
-
如果 someCondition 失败,那么该变量根本不会计算
属性委托要求
必须是同类型、超类型
-
对于 val 只读属性,委托必须提供一个名为 getValue 的函数
-
对于 var 可变属性,委托必须额外提供一个名为 setValue 的函数
翻译规则
-
每个委托属性的实现的背后
-
Kotlin 编译器都会生成辅助属性并委托给它
-
编译后的范例学习
class C {
var prop: Type by MyDelegate()
}
// 看这段
// 这段是由编译器生成的相应代码:
class C {
private val prop$delegate = MyDelegate()
var prop: Type
get() = prop$delegate.getValue(this, this::prop)
set(value: Type) = prop$delegate.setValue(this, this::prop, value)
}
-
注意看$之后的东西
-
代表了内部外部的引用关系
提供委托(自 1.1 起)
-
扩展创建属性实现 所委托对象 的逻辑
-
理解
-
这提供了 委托类 的动态扩展能力
-
便方便的实现委托类的定制化(继承之后)
-
也就增强了委托类的层次感
范例
class ResourceDelegate<T> : ReadOnlyProperty<MyUI, T> {
override fun getValue(thisRef: MyUI, property: KProperty<*>): T { ... }
}
class ResourceLoader<T>(id: ResourceID<T>) {
operator fun provideDelegate(
thisRef: MyUI,
prop: KProperty<*>
): ReadOnlyProperty<MyUI, T> {
checkProperty(thisRef, prop.name)
// 创建委托
return ResourceDelegate()
}
private fun checkProperty(thisRef: MyUI, name: String) { …… }
}
class MyUI {
fun <T> bindResource(id: ResourceID<T>): ResourceLoader<T> { …… }
val image by bindResource(ResourceID.image_id)
val text by bindResource(ResourceID.text_id)
}
-
thisRef
-
必须与 属性所有者 类型(对于扩展属性——指被扩展的类型)相同或者是它的超类型
-
property
-
必须是类型 KProperty<*> 或其超类型。
-
通过范例,可以看到委托解决了
-
显式传递属性名
-
提供了拦截的功能
看一段provideDelegate的生成代码
class C {
var prop: Type by MyDelegate()
}
// 这段代码是当“provideDelegate”功能可用时
// 由编译器生成的代码:
class C {
// 调用“provideDelegate”来创建额外的“delegate”属性
private val prop$delegate = MyDelegate().provideDelegate(this, this::prop)
val prop: Type
get() = prop$delegate.getValue(this, this::prop)
}
-
注意
-
provideDelegate 方法只影响辅助属性的创建
-
不会影响为 getter 或 setter 生成的代码
最近又赶上跳槽的高峰期,好多粉丝,都问我有没有最新大厂面试题,我连日加班好多天,终于整理好了,1000+ 道,20多份大厂面试题大全,我会持续更新中!公众号回复【面试题】即可获取。