Kotlin类委托和属性委托

Kotlin类委托和属性委托

kotlin类委托

使用kotlin类委托

kotlin的类委托主要是接口代理,类似Java中代理模式。委托机制:类A需要实现的操作委托给类B来实现。举个例子:假设有个人A需要打官司但是不会,于是委托给律师B来打官司,在这个过程中A委托了B,B代理A来打官司,委托代理成对出现,一般先有委托再有代理,这个过程也可以通过代码来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 接口 打官司
*/
interface ILawsuit{
fun lawsuit(); // 方法 打官司
}

// 律师
class Lawyer : ILawsuit{
override fun lawsuit() {
println("律师执行打官司")
}
}

// 平民 通过by 关键字委托给律师类
class Civilian : ILawsuit by Lawyer()

fun main(){
val civilian : Civilian = Civilian();
civilian.lawsuit() // 平民执行打官司 实际上是委托给律师来执行
}

通过kotlin字节码反编译,可以很明显的看出其实现方式就是通过Java的代理模式来实现的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 打官司接口
public interface ILawsuit {
void lawsuit();
}
// 律师
public final class Lawyer implements ILawsuit {
public void lawsuit() {
String var1 = "律师执行打官司";
System.out.println(var1);
}
}
// 平民
public final class Civilian implements ILawsuit {
// $FF: synthetic field
private final Lawyer $$delegate_0 = new Lawyer();

public void lawsuit() {
this.$$delegate_0.lawsuit();
}
}
// main执行
public static final void main() {
Civilian civilian = new Civilian();
civilian.lawsuit();
}

覆写委托类方法

有些时候在委托类实现的情况下,又需要自定义方法实现,在这种情况下可以通过覆写委托类方法来实现,具体如以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ExampleList<T> (val innerList : MutableCollection<T> = mutableListOf()) : MutableCollection<T> by innerList{

var count = 0

override fun add(element: T): Boolean { // 覆写方法,实现自定义方法
count ++;
return innerList.add(element) // 代理对象只能够在覆写方法中调用
}

override fun addAll(elements: Collection<T>): Boolean { // 覆写方法,实现自定义方法
count += elements.size
return innerList.addAll(elements) // 代理对象只能够在覆写方法中调用
}
}

fun main(){
val exampleList = ExampleList<Int>()
exampleList.addAll(listOf(1,2,3))
println("已添加${exampleList.count}个元素")
}

kotlin属性委托

定义一个被委托的类

与类委托类似,属性委托也是通过by关键字来定义的,其格式为:var/val 变量 :Type by 委托类,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* kotlin 属性代理
*/

class ProxyExample {

var example : String by Delegate()
}

// 被委托的类 被委托的类必须有getValue 和 setValue的方法
class Delegate{

operator fun getValue(thisRef: Any?,property : KProperty<*>):String{
return "$thisRef,代理读取 ${property.name} 的值"
}

operator fun setValue(thisRef: Any?,property: KProperty<*>,value: String){
print("$thisRef,代理写入 ${property.name} 的值为:$value")
}
}

fun main(){
val proxy = ProxyExample();
println(proxy.example)
proxy.example = "Name"
}

// 运行结果:
chapter7.yangn.five.ProxyExample@76fb509a,代理读取 example 的值
chapter7.yangn.five.ProxyExample@76fb509a,代理写入 example 的值为:Name
Process finished with exit code 0

注意:

对于只读属性,即val定义的变量,它的委托类必须提供一个getValue函数,且函数用operator关键字修饰,getValue有两个参数:

  • thisRef:根据以上运行结果,thisRef指的是当前调用对象(即属性所有者),其类型约束与调用对象一致或者是调用对象的超类型(子类型)
  • property:当前属性所持有者的反射,其类型约束为KProperty<*>或者是KProperty<*>的超类型(子类型)

对于可读写属性,即var定义的变量,它的委托类除了提供getValue函数,还需要提供一个setValue函数,同样的,setValue函数也需要用operator关键字来修饰,setValue函数接收3个参数:

  • thisRef:同getValue()
  • property:同getValue()
  • new value:提供给当前属性用来赋新值的参数,其类型与当前属性一致或其超类型(子类型)

通过字节码将其转为Java代码,查看属性委托其原理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public final class ProxyExample {
@NotNull
private final Delegate example$delegate = new Delegate();

@NotNull
public final String getExample() {
return this.example$delegate.getValue(this, $$delegatedProperties[0]);
}

public final void setExample(@NotNull String var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
this.example$delegate.setValue(this, $$delegatedProperties[0], var1);
}
}

// 委托类
public final class Delegate {
@NotNull
public final String getValue(@Nullable Object thisRef, @NotNull KProperty property) {
Intrinsics.checkParameterIsNotNull(property, "property");
return thisRef + ",代理读取 " + property.getName() + " 的值";
}

public final void setValue(@Nullable Object thisRef, @NotNull KProperty property, @NotNull String value) {
Intrinsics.checkParameterIsNotNull(property, "property");
Intrinsics.checkParameterIsNotNull(value, "value");
String var4 = thisRef + ",代理写入 " + property.getName() + " 的值为:" + value;
System.out.print(var4);
}
}

public static final void main() {
ProxyExample proxy = new ProxyExample();
String var1 = proxy.getExample();
System.out.println(var1);
proxy.setExample("Name");
}

标准委托

在kotlin标准库中也内置了很多工厂方法实现属性的委托。

延迟加载–Lazy()函数

延迟加载lazy()函数的用法,具体如下代码:

1
2
3
4
5
6
class ExampleByLazy{
val example by lazy{
println("lazy action")
"lazy"
}
}

val example by lazylazy是标准库中的一个函数,该函数传入的参数是一个lambda表达式,其返回值是lazy<T>,即属性example实际上是委托给了lazy<T>

1
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

lazy函数返回的是lazy<T>泛型类,但是实际上返回的是SynchronizedLazyImpl<out T>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
private var initializer: (() -> T)? = initializer
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
// final field is required to enable safe publication of constructed instance
private val lock = lock ?: this

override val value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return _v1 as T
}

return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST") (_v2 as T)
} else {
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}
}
}
1
2
@kotlin.internal.InlineOnly
public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value

泛型类lazy<T>实现了一个getValue 的扩展方法,这是被委托类必须要实现的一个方法,getValue函数返回的是lazy<T>中的value属性值,SynchronizedLazyImpl<out T>lazy<T>的具体实现类,因此实际返回的是SynchronizedLazyImpl<out T>中的value属性值,value属性值最终返回的就是lambda表达式返回的值

注意:

  • 泛型类lazy<T>并没有实现setValue的扩展函数,所以委托给lazy<T>的属性只能够使用val来声明而不能够使用var声明
  • lazy函数是懒加载函数,只有第一次调用时能够完整执行lambda函数,之后调用都只是返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ExampleByLazy{
val example by lazy{
println("lazy action") // 只有第一次调用时执行
"lazy" // 每次调用都会返回值
}
}

fun main(){
val exampleByLazy = ExampleByLazy();
println(exampleByLazy.example); // 第一次调用
println(exampleByLazy.example) // 第二次调用
}

****************运行结果*****************************************
lazy action
lazy
lazy
Process finished with exit code 0

可观察属性Observable

顾名思义,Dalegates.obserable()就是可观察的属性,可以对属性变化进行观察和处理,Delegates.observable传入两个参数:

  • initialValue:提供该属性的初始值
  • onChange:提供一个含有三个参数的lambda表达式,用于观察属性变化后对属性变化的额外处理,三个参数分别是property: KProperty<*>, oldValue: T, newValue: T,分别代表被赋值的属性,旧值,新值

Delegates.observable使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Delegates.observable
class ExampleObservable{
var value by Delegates.observable("initValue"){
prop,oldValue,newValue ->
println("被赋值的属性:$prop,旧值:$oldValue,新值:$newValue")
}
}

fun main(){
// Delegates.observable
val observable = ExampleObservable()
observable.value = "nice"
observable.value = "second"
}
*********************************************************
运行结果:
被赋值的属性:var chapter7.yangn.five.ExampleObservable.value: kotlin.String,旧值:initValue,新值:nice
被赋值的属性:var chapter7.yangn.five.ExampleObservable.value: kotlin.String,旧值:nice,新值:second
Process finished with exit code 0

跟踪Delegates.observable代码实现,理解其具体实现过程:

1
2
3
4
5
public inline fun <T> observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit):
ReadWriteProperty<Any?, T> =
object : ObservableProperty<T>(initialValue) {
override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public abstract class ObservableProperty<T>(initialValue: T) : ReadWriteProperty<Any?, T> {
private var value = initialValue

public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value
}

public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
val oldValue = this.value
if (!beforeChange(property, oldValue, value)) {
return
}
this.value = value
afterChange(property, oldValue, value)
}
}

observable是单例类Delegates中的一个方法,observable方法返回了类ObservablePropertyObservableProperty是一个抽象类,所以需要覆写afterChange函数,afterChange函数实际上就是传递进来的onChangelambda表达式,因而demo中ExampleObservablevalue属性实际上是被委托给了ObservableProperty类,这样整个实现就一目了然了

集合map

kotlin为Map实现了getValue扩展函数,所以Map可以作为属性委托类,使用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class ExampleMap(val map: Map<String,Any?>){
val name by map
val age by map
}

fun main(){
// map
val exampleMap = ExampleMap(mapOf("name" to "kotlin","age" to 3))
println(exampleMap.name)
println(exampleMap.age)
}
***********************************************************
运行结果:
kotlin
3
Process finished with exit code 0

Delegates.NotNull

与Delegates.observable用法类似,不过Delegates,NotNull适用于那些无法在初始化阶段就确定属性值的场合。

1
2
3
4
5
6
7
8
9
10
11
12
13
class ExampleNotNull(){
var notNullValue by Delegates.notNull<String>()
}
fun main(){
// not null
val exampleNotNull = ExampleNotNull()
exampleNotNull.notNullValue = "initValue"
println(exampleNotNull.notNullValue)
}
************************************************************
运行结果:
initValue
Process finished with exit code 0

注意:使用Delegates.NotNull如果属性在赋值前访问会抛出异常

1
2
3
4
5
6
Exception in thread "main" java.lang.IllegalStateException: Property notNullValue should be initialized before get.
at kotlin.properties.NotNullVar.getValue(Delegates.kt:62)
at chapter7.yangn.five.ExampleNotNull.getNotNullValue(Second.kt)
at chapter7.yangn.five.SecondKt.main(Second.kt:51)
at chapter7.yangn.five.SecondKt.main(Second.kt)
Process finished with exit code 1