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

我的新博客地址: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提取到一个类中去,让植物和动物都继承自它。

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

  1. 植物和动物继承自同一个类,不太合理
  2. 杂草和毒蛇也具有了取悦于人的能力,也不太合理

这时我们就可以使用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

27

28

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

11

12

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

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

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加上一段额外的信息。

你可能感兴趣的:(scala)