Scala概述(六)合成(2)

本文深入探讨Scala中的Mixin合成机制,包括成员继承、抽象与具体成员的判定规则、父类调用及super关键字的使用。并通过具体示例介绍了如何通过多重继承实现类的扩展。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

成员(Membership

如前所示,Iter 类从StringIteratorRichIterator 同时继承了类成员(members )。简单而言,一个类从以混入合成方式继承Cn with … with C1 ,将会继承其中所有类的成员,同时还可以自定义新的成员。由于Scala 保留了JavaC# 的静态重载机制,因此可能从父类继承同名的方法,也可以再定义同名的方法[1] 。为了判断类C 的一个方法到底是覆盖父类中的同名方法,还是这两个方法并存——即重载的关系,Scala 采用了匹配(matching )法,这也是从JavaC# 中类似的概念衍生来的:简单地说,如果两个类成员同名,并且具有相同的参数类型(如果两个都是方法),就称之为相匹配。

一个类的成员总共两种类型:具体和抽象的,每种类型分别对应一个判定规则:(注:下面几段是说明Scala 语言判断类成员属性的内部机制,不关心这方面细节的可以忽略)

一个类C 的具体成员是指其或其父类的所有具体声明M ,除非在其某个父类(也就是在L(C) )中已有一个匹配的具体成员。

一个类C 的抽象成员是指其或其父类的所有抽象声明M ,除非在C 中已有一个匹配的具体成员,或者其某个父类(也就是在L(C) )中有一个匹配的抽象成员。

这些规则同样决定了一个类C 与其父类之间匹配成员的覆盖关系。首先,具体成员一定覆盖抽象成员。其次,如果MM’ 同为具体成员或抽象成员,且MC 的全序化当中出现在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)

}

想要构造一个针对StringRich 、同步迭代器,可以用这三个类进行合成:

StringIterator(someString) with RichIterator[char]

with SyncIterator[char]

这个合成类从SynchIterator 继承了hasNextNext ,这两个方法都是对其父类的相应方法调用加了一个sychronized() 包装。

由于RichIteratorSyncIterator 定义的方法相互不重合(注:原文是RichIteratorStringIterator ,应该有误),因此它们出现在mixin 中的顺序没有影响,即上例写成这样也是等价的:

StringIterator(someString) with SyncIterator[char]

with RichIterator[char]

 

但是,这里有一个小细节要注意:在SyncIterator 中的super 这个调用并不是静态地绑定到其父类AbsIterator 上,因为显然这是毫无意义的,AbsIterator 定义的nexthasNext 都是抽象方法。实际上,这个super 调用实际上指向这个mixin 合成中的superclassStringIterator 的相应方法。从这个意义上讲,一个mixin 合成的superclass 覆盖了其各个mixin 当中静态声明的超类。这也就意味着super 调用在一个类当中无法被静态解析,必须延迟到一个类被实例化或被继承的时候才能解析出来。这一概念有如下精确定义:

假设CD 的父类,在C 当中的表达式super .M 应该能够静态解析为C 的某个父类当中的成员M ,这样才能保证类型正确。而在D 的语境中(context ,我将其翻译为语境,而不是通常人们翻译的“上下文”。这个问题说来话长,有机会的话会变成一篇文章甚至一本书——译者在这里顺便贩卖一下私货),这个表达式应该表示一个与M 相匹配的M’ ,这个成员应该在D 的全序当中位于C 之后的某个类里定义。

最后注意一点:在JavaC# 等语言中,上述SyncIterator 当中的这种super 调用明显是不合法的,因为它会被指派为父类当中的抽象成员(方法)。如同我们在上面看到的,这种构造在scala 中是合法的,只要保证一个前提,那就是这个类所出现的语境当中,其super 调用所访问的父类成员必须是具体定义了的。这一点是由SyncIterator 当中的abstractoverride 这两个关键字保证的。在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

}

}

我们可以将这两种迭代子( sychronizedlogged )通过 mixin 组合在一起:

class Iter2 extends StringIterator(someString)

with SyncIterator[char]

with LoggedIterator[char]

在这里, Iter2 的全序化是:

{ Iter2, LoggedIterator, SyncIterator,

StringIterator, AbsIterator, AnyRef, Any }

这样一来, Iter2next 方法继承自 LoggedIterator ,而该方法中的 super.next 则指向 SyncIteratornext 方法,而后者当中的 super.next 则最终引用 StringIteratornext 方法。

如果想对记录日志的动作进行同步,仅需要把两个 mixin 的顺序反过来即可实现:

class Iter2 extends StringIterator(someString)

with LoggedIterator[char]

with SyncIterator[char]

无论哪种情况, Iter2 next 方法当中 super 的调用都遵循其全序当中的父类顺序。



[1] 有人可能反对这种设计方式,认为这样太复杂,但是为了保证互操作性,这样做是必须的,例如一个 Scala 类继承一个 Java Swing 类的时候。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值