Kotlin中的by关键字

参考链接

《Kotlin - By 关键字介绍》
《Kotlin实战》
《Kotlin中文文档》
《java_my_life的博客》

装饰器模式

装饰器模式又名包装模式,来以对客户透明的方式(客户端并不觉得修饰前后的区别)扩展对象的功能,是继承关系的一种替代方案。

在装饰模式中的角色有:

  • 抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
  • 具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。
  • 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
  • 具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
interface CanBeWorn{
void worn();
}

class UnderWear implements CanBeWorn{

@Override
public void worn() {
System.out.print("穿内衣");
}
}

/**
* 装饰器模式的核心
* 它的子类都可以代理执行注入类的worn()方法
*/
class Mother implements CanBeWorn{

private CanBeWorn canBeWorn;

public Mother(CanBeWorn canBeWorn){
this.canBeWorn = canBeWorn;
}

@Override
public void worn() {
canBeWorn.worn();
}
}

class Coat extends Mother{

public Coat(CanBeWorn canBeWorn) {
super(canBeWorn);
}

@Override
public void worn() {
super.worn();
System.out.println("穿上衣");
}
}

class Pants extends Mother{

public Pants(CanBeWorn canBeWorn) {
super(canBeWorn);
}

@Override
public void worn() {
super.worn();
System.out.println("穿裤子");
}
}

这个例子举的有些不太符合,但是基本上能说明装饰模式的过程。

1
2
3
CanBeWorn canBeWorn = new UnderWear();
CanBeWorn coat = new Coat(canBeWorn);
CanBeWorn pants = new Pants(coat);

装饰模式的简化

大多数情况下,装饰模式的实现都要比上面给出的示意性例子要简单。

如果只有一个ConcreteComponent类,那么可以考虑去掉抽象的Component类(接口),把Decorator作为一个ConcreteComponent子类。

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
38
39
40
41
42
43
44
45
46
class UnderWear{

public void worn() {
System.out.print("穿内衣");
}
}

class Mother extends UnderWear{

private UnderWear underWear;

public Mother(UnderWear underWear){
this.underWear = underWear;
}

@Override
public void worn() {
underWear.worn();
}
}

class Coat extends Mother{

public Coat(UnderWear underWear) {
super(underWear);
}

@Override
public void worn() {
super.worn();
System.out.println("穿上衣");
}
}

class Pants extends Mother{

public Pants(UnderWear underWear) {
super(underWear);
}

@Override
public void worn() {
super.worn();
System.out.println("穿裤子");
}
}

类的代理

上面的代码用Kotlin实现

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
interface CanBeWorn {
fun worn()
}

class UnderWear : CanBeWorn {

override fun worn() {
println("穿内衣")
}
}

abstract class Mother(val canBeWorn: CanBeWorn) : CanBeWorn {

override fun worn() {
canBeWorn.worn()
}
}

class Coat(canBeWorn: CanBeWorn) : Mother(canBeWorn) {

override fun worn() {
super.worn()
println("穿上衣")
}
}

class Pants(canBeWorn: CanBeWorn) : Mother(canBeWorn) {

override fun worn() {
super.worn()
println("穿裤子")
}
}

fun main(args: Array<String>){
Pants(Coat(UnderWear())).worn()
}

在Kotlin中可以使用by关键字将接口的实现委托到另一个对象。

所以上面的

1
2
3
4
5
6
abstract class Mother(val canBeWorn: CanBeWorn) : CanBeWorn {

override fun worn() {
canBeWorn.worn()
}
}

可以修改如下

1
abstract class Mother(val canBeWorn: CanBeWorn) : CanBeWorn by canBeWorn

覆盖代理成员

编译器会使用 override 覆盖的实现而不是委托对象中的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
interface Base {
fun printMessage()
fun printMessageLine()
}

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

class Derived(b: Base) : Base by b {
override fun printMessage() { print("abc") }
}

fun main() {
val b = BaseImpl(10)
Derived(b).printMessage()
Derived(b).printMessageLine()
}

>>> abc10

注意,以这种方式重写的成员不会在委托对象的成员中调用 ,委托对象的成员只能访问其自身对接口成员实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
interface Base {
val message: String
fun print()
}

class BaseImpl(val x: Int) : Base {
override val message = "BaseImpl: x = $x"
override fun print() { println(message) }
}

class Derived(b: Base) : Base by b {
// 在 b 的 `print` 实现中不会访问到这个属性
override val message = "Message of Derived"
}

fun main() {
val b = BaseImpl(10)
val derived = Derived(b)
derived.print()
println(derived.message)
}

>>> BaseImpl: x = 10
>>> Message of Derived

属性委托

不仅可以类代理,还可以代理类属性,监听属性变化。

1
2
3
class Example {
var p: String by Delegate()
}

语法是: val/var <属性名>: <类型> by <表达式>。在 by 后面的表达式是该 委托, 因为属性对应的 get()(与 set())会被委托给它的 getValue() 与 setValue() 方法。 属性的委托不必实现任何的接口,但是需要提供一个 getValue() 函数(与 setValue()——对于 var 属性)。 例如:

1
2
3
4
5
6
7
8
9
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}

operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
}

当我们从委托到一个 Delegate 实例的 p 读取时,将调用 Delegate 中的 getValue() 函数, 所以它第一个参数是读出 p 的对象、第二个参数保存了对 p 自身的描述 (例如你可以取它的名字)。 例如:

1
2
3
4
val e = Example()
println(e.p)

>>> Example@33a17727, thank you for delegating ‘p’ to me!

标准委托

延迟属性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() {
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
class User {
var name: String by Delegates.observable("<no name>") {
prop, old, new ->
println("$old -> $new")
}
}

fun main() {
val user = User()
user.name = "first"
user.name = "second"
}

>>> <no name> -> first
>>> first -> second