Kotlin委托

代码星冰乐

专注成就未来

首页 归档 关于

Kotlin委托

Sep 3, 2017 | ChanghuiN | Kotlin | 阅读
文章目录
  1. 1. 类委托
  2. 2. 委托属性
    1. 2.1. 属性委托要求
    2. 2.2. 翻译规则
  3. 3. 标准委托
    1. 3.1. 延迟属性 Lazy
    2. 3.2. 可观察属性 Observable
    3. 3.3. 特殊的observable Vetoable
    4. 3.4. NotNull 委托
    5. 3.5. 把属性储存在映射中
  4. 4. 使用场景

作 者:ChanghuiN
原文链接:https://www.hchstudio.cn/article/2017/9855/
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。


由于版权原因,请阅读原文 --> Kotlin委托

关注我们

作 者:ChanghuiN
原文链接:https://www.hchstudio.cn/article/2017/9855/
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。

作 者:ChanghuiN
原文链接:https://www.hchstudio.cn/article/2017/9855/
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。

Kotlin中有委托,这个C#中也有,不过对于学Java的童鞋来说,这是什么鬼啊,到底是干什么用的…
在委托模式中,当有两个对象参与处理同一个请求是,接受请求的对象将请求委托给另一个对象来处理。委托模式已证明是实现继承的一个很好的替代方式。Kotlin中委托分为类委托和委托属性,Kotlin官方库也封装了一些常用的委托。

类委托

类 Derived 可以继承一个接口 Base,并将其所有共有的方法委托给一个指定的对象,也就是说把类 Derived 因继承而需要实现的方法委托给一个对象,从而不需要在该类内显式的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface Base {
fun print()
}

class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}

class Derived(b: Base) : Base by b

fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // prints 10
}

如果 Derived 类没有写‘by b’,那么就必须重写print方法,否则编译器会报错。

委托属性

语法:val/var <属性名>: <类型> by <表达式>
属性的委托不必实现任何的接口,但是需要提供一个 getValue() 函数(和 setValue()——对于 var 属性),因为属性对应的 get()(和 set())会被委托给它的 getValue() 和 setValue() 方法。也可以直接继承 ReadWriteProperty ,实现其中的方法,这样就避免了自己手写可能出现的错误,例如 Kotlin 源码中这样实现判空的委托属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public object Delegates {
public fun <T: Any> notNull(): ReadWriteProperty<Any?, T> = NotNullVar()
}

private class NotNullVar<T: Any>() : ReadWriteProperty<Any?, T> {
private var value: T? = null

public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
}

public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = value
}
}

其中 NotNullVar 继承了 ReadWriteProperty,并实现了他的两个方法,而Delegates.notNull() 属于委托属性。

属性委托要求

对于一个只读属性(即 val 声明的),委托必须提供一个名为 getValue 的函数,该函数接受以下参数(可以继承 ReadOnlyProperty 实现该方法):

  • thisRef —— 必须与 属性所有者 类型(对于扩展属性——指被扩展的类型)相同或者是它的超类型,
  • property —— 必须是类型 KProperty<*> 或其超类型,

对于一个可变属性(即 var 声明的),委托必须额外提供一个名为 setValue 的函数,该函数接受以下参数(可以继承 ReadWriteProperty 实现该方法):

  • thisRef —— 同 getValue(),
  • property —— 同 getValue(),
  • new value —— 必须和属性同类型或者是它的超类型。

getValue() 或/和 setValue() 函数可以通过委托类的成员函数提供或者由扩展函数提供。 当你需要委托属性到原本未提供的这些函数的对象时后者会更便利。 两函数都需要用 operator 关键字来进行标记。

翻译规则

在每个委托属性的实现的背后,Kotlin 编译器都会生成辅助属性并委托给它。 例如,对于属性 prop,生成隐藏属性 prop$delegate,而访问器的代码只是简单地委托给这个附加属性:

1
2
3
4
5
6
7
8
9
10
11
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)
}

Kotlin 编译器在参数中提供了关于 prop 的所有必要信息:第一个参数 this 引用到外部类 C 的实例而 this::prop 是 KProperty 类型的反射对象,该对象描述 prop 自身。

标准委托

延迟属性 Lazy

lazy() 是接受一个 lambda 并返回一个 Lazy 实例的函数,返回的实例可以作为实现延迟属性的委托: 第一次调用 get() 会执行已传递给 lazy() 的 lambda 表达式并记录结果, 后续调用 get() 只是返回记录的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
val lazyValue: String by lazy {
println("computed!")
"Hello"
}

fun main(args: Array<String>) {
println(lazyValue)
println(lazyValue)
}
// 输出:
computed!
Hello
Hello

可观察属性 Observable

当属性值改变的时候运行函数方法。
Delegates.observable() 接受两个参数:初始值和修改时处理程序(handler)。 每当我们给属性赋值时会调用该处理程序(在赋值后执行)。它有三个参数:被赋值的属性、旧值和新值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import kotlin.properties.Delegates

class User {
var name: String by Delegates.observable("<no name>") {
prop, old, new ->
println("$old -> $new")
}
}

fun main(args: Array<String>) {
val user = User()
user.name = "first"
user.name = "second"
}
输出:
<no name> -> first
first -> second

特殊的observable Vetoable

他让你决定这个值是否需要保存,用于在真正保存之前的一些条件判断。

1
2
3
4
var positiveNumber = Delegates.vetoable(0) {
d, old, new ->
new >= 0
}

NotNull 委托

他会含有一个可空变量并会在我们设置这个属性的时候分配一个真实的值。如果这个值在被获取之前没有被分配,它就会抛出异常。

1
2
3
4
5
6
7
8
9
10
class App: Application() {
companion object {
var instance: App by Delegates.netNull()
}

override fun onCreate() {
super.onCreate()
instance = this
}
}

把属性储存在映射中

一个常见的用例是在一个映射(map)里存储属性的值。 这经常出现在像解析 JSON 或者做其他“动态”事情的应用中。 在这种情况下,你可以使用映射实例自身作为委托来实现委托属性。

1
2
3
4
5
6
7
8
9
class User(val map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}

val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))

使用场景

下面来总结一下委托使用的场景:

  • 有多个类或属性重复使用同一段代码。
  • 实现继承的代替方式
  • 可以使用标准委托,来实现一些特殊的功能,当然,自己也可以自定义一些
  • 其余的有待小伙伴们一起补充吆~

Vetoable\Not null

关注我们

作 者:ChanghuiN
原文链接:https://www.hchstudio.cn/article/2017/9855/
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。

分享
AndroidKotlin
shell脚本实现自动保留最近n次备份记录扒一扒spring,dom4j实现模拟实现读取xml
微信关注我们
分类
  • Android8
  • Go4
  • Java59
  • Kafka,Java1
  • Kotlin2
  • Linux1
  • MapReduce1
  • Python2
  • Raft1
  • Redis1
  • ThreadPoolExecutor1
  • go1
  • 工具1
  • 总结8
  • 旅游日记1
标签
Nginx ChanghuiN haifeiWu Android Java 设计模式 hexo Kotlin 算法 MySQL 源码解析 Python Redis golang web Kafka 配置中心 总结 性能优化 旅游日记 Shell Go 问题排查 译文 Docker Spring Boot 工具 学习笔记 WebFlux 性能测试 go 散列表 源码 netty Raft
最近文章
  • Kafka的日志复制机制
  • 从20到21
  • go 并发编程
  • 【译】了解Linux CPU负载-您何时应该担心?
  • Zookeeper 与分布式锁
  • 基于Redis的分布式锁到底安全吗?
  • 【译】Raft 学生指南
  • ThreadPoolExecutor 的简单梳理
  • MapReduce 的简单实现
  • 使用 Map 实现策略模式
福利专区
    免费SSL证书
      阿里云红包
        腾讯云专属福利
        Copyright © 2021 代码星冰乐. Powered by ChanghuiN. 版权所有 晋ICP备15001365号
        特别感谢: 云服务器服务商 、 CDN 服务商