hangscer

trait的堆叠

2017/05/07

对于多重继承语言来说,调用相同的方法,编译规则会决定哪个超类胜出,从而调用哪个方法.
特质可以多继承多个特质,一个类也可以继承多个特质.对特质来说,方法的调用是类和混入的特质的线性化所决定.

构造顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
trait T1{
println("T1")
}
trait T2 extends T1{
println("T2")
}
trait T3 extends T1{
println("T3")
}
trait T4 extends T3{
println("T4")
}
class C2 extends T1 with T2 with T3 with T4{
println("C2")
}
val c2=new C2
//output
//T1
//T2
//T3
//T4
//C2


我们可以看出构造顺序是从被混入或者继承的类或特质列表从左向右,最后实例化自己.原因是由于确保父类型先于继承类型被构造,在构造过程中,常常需要使用父类型的字段和方法.

方法调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
trait T1{
def m=println("T1")
}
trait T2 extends T1{
override def m: Unit = {println("T2");super.m}
}
trait T3 extends T1{
override def m: Unit = {println("T3");super.m}
}
trait T4 extends T3{
override def m: Unit = {println("T4");super.m}
}
class C2 extends T1 with T2 with T3 with T4{
override def m: Unit = {println("C2");super.m}
}
val c2=new Test1.C2
c2.m
//output
//C2
//T4
//T3
//T2
//T1

可以看出trait中的m方法依照声明顺序从右向左被调用.
把程序再改一下:删除T4中的super.m调用链:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
trait T1{
def m=println("T1")
}
trait T2 extends T1{
override def m: Unit = {println("T2");super.m}
}
trait T3 extends T1{
override def m: Unit = {println("T3");super.m}
}
trait T4 extends T3{
override def m: Unit = {println("T4");}
}
class C2 extends T1 with T2 with T3 with T4{
override def m: Unit = {println("C2");super.m}
}
val c2=new Test1.C2
c2.m
//output
//C2
//C4

原因在于:当在某个类中调用super,被调用的方法就是方法链的下一节,除了最后一个调用super之外的方法,对外表现则是trait堆叠的行为.

线性化算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class C1{
def m=print("C1 ")
}
trait T1 extends C1{
override def m: Unit = {print("T1 ");super.m}
}
trait T2 extends C1{
override def m: Unit = {print("T2 ");super.m}
}
trait T3 extends C1{
override def m: Unit = {print("T3 ");super.m}
}
class C2A extends T2{
override def m: Unit = {print("C2A ");super.m}
}
class C2 extends C2A with T1 with T2 with T3{
override def m: Unit = {print("C2 ");super.m}
}
val c2=new C2
c2.m
print("AnyRef ")
print("Any ")
//C2 T3 T1 C2A T2 C1 AnyRef Any
C2类型的线性化计算过程
C2 添加当前实例类型
C2,T3,C1 添加T3的线性列表,T3继承C1
C2,T3,C1,T2,C1 添加T2的线性列表
C2,T3,C1,T2,C1,T1,C1 添加T1的线性列表
C2,T3,C1,T2,C1,T1,C1,C2A,T2,C1 添加C2A的线性列表
C2,T3,T2,T1,C2A,T2,C1 移除C1的元素,保留最后一个
C2,T3,T1,C2A,T2,C1 移除T2元素,保留最后一个
C2,T3,T1,C2A,T2,C1,AnyRef,Any OK