您的位置:首页 > 大数据 > 人工智能

Scala中的语言特性是如何实现的(3) -- Trait

2013-12-21 13:06 579 查看
我的新博客地址:http://cuipengfei.me/blog/2013/10/13/scala-trait/

我在Coursera上跟了一门叫做Functional Programming Principles in Scala的课程,是由Scala的作者Martin Odersky讲授的。其中第三周的作业中使用到了Scala的trait这个语言特性。

我以前熟知的语言都没有类似的特性(Ruby的mixin和Scala的trait很像,但是Ruby我不熟),所以这周的博客就分析一下这个语言特性是如何实现的。

trait

在讲trait的实现机制之前,先看一个使用trait的例子。 假设我们有以下几个类:

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

abstract class Plant {
def photosynthesis = println("Oh, the sunlight!")
}
class Rose extends Plant {
def smell = println("Good!")
def makePeopleHappy = println("People like me")
}
class Ruderal extends Plant {
def grow = println("I take up all the space!")
}
abstract class Animal {
def move = println("I can move!")
}
class Dog extends Animal {
def bark = println("Woof!")
def makePeopleHappy = println("People like me")
}
class Snake extends Animal {
def bite = println("I am poisonous!")
}

植物家族有玫瑰和杂草。

动物家族有狗和毒蛇。

仔细观察可以发现,玫瑰和狗有一个共同的行为,它们都可以取悦人类,这个行为是用完全一样的代码实现的。

如何把Rose和Dog中的重复代码消除掉呢?有一种潜在的解决方案: 把makePeopleHappy提取到一个类中去,让植物和动物都继承自它。

这么做虽然消除了重复代码但有两个明显的缺点:

植物和动物继承自同一个类,不太合理

杂草和毒蛇也具有了取悦于人的能力,也不太合理

这时我们就可以使用trait,它没有上面提到的两个缺点。

1
2
3
4
5
6
7
8
9
10
11

trait PeoplePleaser {
def makePeopleHappy = println("People like me")
}
class Rose extends Plant with PeoplePleaser {
def smell = println("Good!")
}
class Dog extends Animal with PeoplePleaser {
def bark = println("Woof!")
}

我们定义一个trait,把makePeopleHappy置于其中,让Rose和Dog都with这个trait。然后就可以写这样的代码来调用它们了:

1
2

new Rose().makePeopleHappy
new Dog().makePeopleHappy

这样我们就解决了重复代码的问题,而且没有触及已存在的继承关系。

现在看看trait的实现机制吧,我们开始反编译!

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
2728
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

public abstract interface PeoplePleaser
{
public abstract void makePeopleHappy();
}
public abstract class PeoplePleaser$class
{
public static void makePeopleHappy(PeoplePleaser $this)
{
Predef..MODULE$.println("People like me");
}
public static void $init$(PeoplePleaser $this)
{
}
}
public class Rose extends Plant
implements PeoplePleaser
{
public void makePeopleHappy()
{
PeoplePleaser$class.makePeopleHappy(this);
}
public void smell() { Predef..MODULE$.println("Good!"); }
public Rose()
{
PeoplePleaser.class.$init$(this);
}
}
public class Dog extends Animal
implements PeoplePleaser
{
public void makePeopleHappy()
{
PeoplePleaser$class.makePeopleHappy(this);
}
public void bark() { Predef..MODULE$.println("Woof!"); }
public Dog()
{
PeoplePleaser.class.$init$(this);
}
}

真相大白了,PeoplePleaser被编译成了一个接口加一个抽象类。Rose和Dog实现这个接口,并通过调用抽象类中的静态方法来实现了makePeopleHappy。

很有趣的一点是Rose和Dog在调用静态方法时都把this传了进去,为什么呢?我们把原来的代码改成这样来看:

1
2
3
4
5
6
7
8
9
10
1112
13
14
15
16
17

trait PeoplePleaser {
val moreMessage = ""
def makePeopleHappy = println("People like me. " + moreMessage)
}
class Rose extends Plant with PeoplePleaser {
override val moreMessage = "Because I smell nice."
def smell = println("Good!")
}
class Dog extends Animal with PeoplePleaser {
override val moreMessage = "Because I fetch balls."
def bark = println("Woof!")
}

我们给makePeopleHappy加上一段额外的信息。 现在再次反编译。

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
2728
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

public abstract interface PeoplePleaser
{
public abstract void objsets$PeoplePleaser$_setter_$moreMessage_$eq(String paramString);
public abstract String moreMessage();
public abstract void makePeopleHappy();
}
public abstract class PeoplePleaser$class
{
public static void makePeopleHappy(PeoplePleaser $this)
{
Predef..MODULE$.println(new StringBuilder()
.append("People like me. ")
.append($this.moreMessage()).toString());
}
public static void $init$(PeoplePleaser $this)
{
$this.objsets$PeoplePleaser$_setter_$moreMessage_$eq("");
}
}
public class Rose extends Plant
implements PeoplePleaser
{
private final String moreMessage;
public void objsets$PeoplePleaser$_setter_$moreMessage_$eq(String x$1)
{
}
public void makePeopleHappy()
{
PeoplePleaser$class.makePeopleHappy(this);
}
public String moreMessage() { return this.moreMessage; }
public void smell() {
Predef..MODULE$.println("Good!");
}
public Rose()
{
PeoplePleaser.class.$init$(this);
this.moreMessage = "Because I smell nice.";
}
}
public class Dog extends Animal
implements PeoplePleaser
{
private final String moreMessage;
public void objsets$PeoplePleaser$_setter_$moreMessage_$eq(String x$1)
{
}
public void makePeopleHappy()
{
PeoplePleaser$class.makePeopleHappy(this);
}
public String moreMessage() { return this.moreMessage; }
public void bark() {
Predef..MODULE$.println("Woof!");
}
public Dog()
{
PeoplePleaser.class.$init$(this);
this.moreMessage = "Because I fetch balls.";
}
}

现在就清楚了,抽象类中的静态方法可能会依赖于各个实例不同的状态,所以需要把this传递进去。 这样我们才能够给makePeopleHappy加上一段额外的信息。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: