Kotlin Vocabulary | Kotlin 委托代理
有时候,完成一些工作的方法是将它们委托给别人。这里不是在建议您将自己的工作委托给朋友去做,而是在说将一个对象的工作委托给另一个对象。
当然,委托在软件行业不是什么新鲜名词。委托 (Delegation) 是一种设计模式,在该模式中,对象会委托一个助手 (helper) 对象来处理请求,这个助手对象被称为代理。代理负责代表原始对象处理请求,并使结果可用于原始对象。
委托 (Delegation) https://en.wikipedia.org/wiki/Delegation_pattern
Kotlin 不仅支持类和属性的代理,其自身还包含了一些内建代理,从而使得实现委托变得更加容易。
类代理
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class ListWithTrash <T>(
private val innerList: MutableList<T> = ArrayList<T>()
) : MutableCollection<T> by innerList {
var deletedItem : T? = null
override fun remove(element: T): Boolean {
deletedItem = element
return innerList.remove(element)
}
fun recover(): T? {
return deletedItem
}
}
by 关键字告诉 Kotlin 将 MutableList 接口的功能委托给一个名为 innerList 的内部 ArrayList。通过桥接到内部 ArrayList 对象方法的方式,ListWithTrash 仍然支持 MutableList 接口中的所有函数。与此同时,现在您可以添加自己的行为了。
工作原理
让我们看看这一切是如何工作的。如果您去查看 ListWithTrash 字节码所反编译出的 Java 代码,您会发现 Kotlin 编译器其实创建了一些包装函数,并用它们调用内部 ArrayList 对象的相应函数:
public final class ListWithTrash implements Collection, KMutableCollection {
@Nullable
private Object deletedItem;
private final List innerList;
@Nullable
public final Object getDeletedItem() {
return this.deletedItem;
}
public final void setDeletedItem(@Nullable Object var1) {
this.deletedItem = var1;
}
public boolean remove(Object element) {
this.deletedItem = element;
return this.innerList.remove(element);
}
@Nullable
public final Object recover() {
return this.deletedItem;
}
public ListWithTrash() {
this((List)null, 1, (DefaultConstructorMarker)null);
}
public int getSize() {
return this.innerList.size();
}
// $FF: 桥接方法
public final int size() {
return this.getSize();
}
//…...
}
注意: 为了在生成的代码中支持类代理,Kotlin 编译器使用了另一种设计模式——装饰者模式。在装饰者模式中,装饰者类与被装饰类使用同一接口。装饰者会持有一个目标类的内部引用,并且包装 (或者装饰) 接口提供的所有公共方法。
装饰者模式 https://en.wikipedia.org/wiki/Decorator_pattern
属性代理
除了类代理,您还可以使用 by 关键字进行属性代理。通过使用属性代理,代理会负责处理对应属性 get 与 set 函数的调用。这一特性在您需要在其他对象间复用 getter/setter 逻辑时十分有用,同时也能让您可以轻松地对简单支持字段的功能进行扩展。
让我们假设您有一个 Person 类型,定义如下:
class Person(var name: String, var lastname: String)
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class Person(name: String, var lastname: String) {
var name: String = name
set(value) {
name = value.toLowerCase().capitalize()
updateCount++
}
var updateCount = 0
}
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class Person(name: String, lastname: String) {
var name: String = name
set(value) {
name = value.toLowerCase().capitalize()
updateCount++
}
var lastname: String = lastname
set(value) {
lastname = value.toLowerCase().capitalize()
updateCount++
}
var updateCount = 0
}
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class Person(name: String, lastname: String) {
var name: String by FormatDelegate()
var lastname: String by FormatDelegate()
var updateCount = 0
}
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class FormatDelegate : ReadWriteProperty<Any?, String> {
private var formattedString: String = ""
override fun getValue(
thisRef: Any?,
property: KProperty<*>
): String {
return formattedString
}
override fun setValue(
thisRef: Any?,
property: KProperty<*>,
value: String
) {
formattedString = value.toLowerCase().capitalize()
}
}
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
override fun setValue(
thisRef: Any?,
property: KProperty<*>,
value: String
) {
if (thisRef is Person) {
thisRef.updateCount++
}
formattedString = value.toLowerCase().capitalize()
}
工作原理
public final class Person {
// $FF: 合成字段
static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Reflection.getOrCreateKotlinClass(Person.class), "name", "getName()Ljava/lang/String;")), (KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Reflection.getOrCreateKotlinClass(Person.class), "lastname", "getlastname()Ljava/lang/String;"))};
@NotNull
private final FormatDelegate name$delegate;
@NotNull
private final FormatDelegate lastname$delegate;
private int updateCount;
@NotNull
public final String getName() {
return this.name$delegate.getValue(this, $$delegatedProperties[0]);
}
public final void setName(@NotNull String var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
this.name$delegate.setValue(this, $$delegatedProperties[0], var1);
}
//...
}
person.lastname = “Smith”
// 调用生成的 setter,增加数量
println(“Update count is $person.count”)
一本手册尽览 Android 11 最新特性与开发技巧
更有成功心得助您举一反三
☟ 即刻下载 ☟
推荐阅读