成员(Membership )
如前所示,Iter 类从StringIterator 和RichIterator 同时继承了类成员(members )。简单而言,一个类从以混入合成方式继承Cn with … with C1 ,将会继承其中所有类的成员,同时还可以自定义新的成员。由于Scala 保留了Java 和C# 的静态重载机制,因此可能从父类继承同名的方法,也可以再定义同名的方法[1] 。为了判断类C 的一个方法到底是覆盖父类中的同名方法,还是这两个方法并存——即重载的关系,Scala 采用了匹配(matching )法,这也是从Java 和C# 中类似的概念衍生来的:简单地说,如果两个类成员同名,并且具有相同的参数类型(如果两个都是方法),就称之为相匹配。
一个类的成员总共两种类型:具体和抽象的,每种类型分别对应一个判定规则:(注:下面几段是说明Scala 语言判断类成员属性的内部机制,不关心这方面细节的可以忽略)
一个类C 的具体成员是指其或其父类的所有具体声明M ,除非在其某个父类(也就是在L(C) )中已有一个匹配的具体成员。
一个类C 的抽象成员是指其或其父类的所有抽象声明M ,除非在C 中已有一个匹配的具体成员,或者其某个父类(也就是在L(C) )中有一个匹配的抽象成员。
这些规则同样决定了一个类C 与其父类之间匹配成员的覆盖关系。首先,具体成员一定覆盖抽象成员。其次,如果M 和M’ 同为具体成员或抽象成员,且M 在C 的全序化当中出现在M’ 之前,则M 覆盖M’ 。
父类调用(Super Calls )
我们考虑设计一个同步迭代器,也就是其操作在多线程之间是互斥的。
trait SyncIterator[T] extends AbsIterator[T] {
abstract override def hasNext: boolean =
synchronized(super .hasNext)
abstract override def next: T =
synchronized(super .next)
}
想要构造一个针对String 的Rich 、同步迭代器,可以用这三个类进行合成:
StringIterator(someString) with RichIterator[char]
with SyncIterator[char]
这个合成类从SynchIterator 继承了hasNext 和Next ,这两个方法都是对其父类的相应方法调用加了一个sychronized() 包装。
由于RichIterator 和SyncIterator 定义的方法相互不重合(注:原文是RichIterator 和StringIterator ,应该有误),因此它们出现在mixin 中的顺序没有影响,即上例写成这样也是等价的:
StringIterator(someString) with SyncIterator[char]
with RichIterator[char]
但是,这里有一个小细节要注意:在SyncIterator 中的super 这个调用并不是静态地绑定到其父类AbsIterator 上,因为显然这是毫无意义的,AbsIterator 定义的next 和hasNext 都是抽象方法。实际上,这个super 调用实际上指向这个mixin 合成中的superclass :StringIterator 的相应方法。从这个意义上讲,一个mixin 合成的superclass 覆盖了其各个mixin 当中静态声明的超类。这也就意味着super 调用在一个类当中无法被静态解析,必须延迟到一个类被实例化或被继承的时候才能解析出来。这一概念有如下精确定义:
假设C 是D 的父类,在C 当中的表达式super .M 应该能够静态解析为C 的某个父类当中的成员M ,这样才能保证类型正确。而在D 的语境中(context ,我将其翻译为语境,而不是通常人们翻译的“上下文”。这个问题说来话长,有机会的话会变成一篇文章甚至一本书——译者在这里顺便贩卖一下私货),这个表达式应该表示一个与M 相匹配的M’ ,这个成员应该在D 的全序当中位于C 之后的某个类里定义。
最后注意一点:在Java 或C# 等语言中,上述SyncIterator 当中的这种super 调用明显是不合法的,因为它会被指派为父类当中的抽象成员(方法)。如同我们在上面看到的,这种构造在scala 中是合法的,只要保证一个前提,那就是这个类所出现的语境当中,其super 调用所访问的父类成员必须是具体定义了的。这一点是由SyncIterator 当中的abstract 和override 这两个关键字保证的。在scala 中,abstract override 这两个关键字成对出现在方法定义中,表明这个方法并没有获得完全的定义,因为它覆盖(并使用)了其父类当中的抽象成员。一个类如果有非完整定义的成员,它自身必须是抽象类,其子类必须将这些非完整定义的成员重新定义,才能进行实例化。
对 super 的调用可以是级联的,因此要遵从类的全序化(这是 Scala 的混入合成方式与多重继承方式之间最主要的差异)。例如,考虑另一个与 SyncIterator 类似的类,它将其返回的每个元素都打印到标准输出上:
trait LoggedIterator[T] extends AbsIterator[T] {
abstract override def next: T = {
val x = super .next; System.out.println(x); x
}
}
我们可以将这两种迭代子( sychronized 和 logged )通过 mixin 组合在一起:
class Iter2 extends StringIterator(someString)
with SyncIterator[char]
with LoggedIterator[char]
在这里, Iter2 的全序化是:
{ Iter2, LoggedIterator, SyncIterator,
StringIterator, AbsIterator, AnyRef, Any }
这样一来, Iter2 的 next 方法继承自 LoggedIterator ,而该方法中的 super.next 则指向 SyncIterator 的 next 方法,而后者当中的 super.next 则最终引用 StringIterator 的 next 方法。
如果想对记录日志的动作进行同步,仅需要把两个 mixin 的顺序反过来即可实现:
class Iter2 extends StringIterator(someString)
with LoggedIterator[char]
with SyncIterator[char]
无论哪种情况, Iter2 的 next 方法当中 super 的调用都遵循其全序当中的父类顺序。