Scala:abstract override

2018-02-15  本文已影响59人  Azur_wxj

定义

官方文档对于abstract override的定义是:

The override modifier has an additional significance when combined with the abstract modifier. That modifier combination is only allowed for value members of traits.
We call a member M of a template incomplete if it is either abstract (i.e. defined by a declaration), or it is labeled abstractand override and every member overridden by M is again incomplete.
Note that the abstract override modifier combination does not influence the concept whether a member is concrete or abstract. A member is abstract if only a declaration is given for it; it is concrete if a full definition is given.

这里面有三个要点:

注意到M被abstract override修饰时,如果M重载的成员是具体的,那么M就不是 incomplete

class Father{
    def name="Father"
}
trait Son extends Father{
    abstract override def name="Son"
}
val son=new Son{}
son.name //"Son"

上面代码中,特质Son继承了类Father,并且方法name被重载。尽管nameabstract override修饰,但是因为name重载的方法(即Fathername)并不是 incomplete ,所以特质Son的方法name也不是 incomplete
这同时也说明了,尽管nameabstract override修饰,但是因为有具体定义,所以它是具体的而不是抽象的。

另外注意到语句val son=new Son{}Son是特质不能被实例化,但是此处写法实际上类似于Java一样声明了一个匿名类,因为Son中没有抽象成员,所以花括号为空。

多提一句,在官方文档中abstractoverride定义分别是:

abstract
The abstract modifier is used in class definitions. It is redundant for traits, and mandatory for all other classes which have incomplete members. Abstract classes cannot be instantiated **with a constructor **invocation unless followed by mixins and/or a refinement which override all incomplete members of the class. Only abstract classes and traits can have abstract term members.

要点:

override
The override modifier applies to class member definitions or declarations. It is mandatory for member definitions or declarations that override some other concrete member definition in a parent class. If an override modifier is given, there must be at least one overridden member definition or declaration (either concrete or abstract).

要点:

说明

abstract override应该兼具abstractoverride两种特性。
首先,如果特质的超类中不存在M方法,则特质中定义M方法时不能使用abstract override,因为会违反override的定义。

其次考虑下面的例子:

abstract class Animal{
    def bark    //abstract method
}
trait Dog extends Animal{
    abstract override def bark=println("WoooW!")  //abstract override
}
new Dog{}.bark 

error: object creation impossible, 
  since method bark in trait Dog of type => Unit 
  is marked `abstract' and `override', 
  but no concrete implementation could be found in a base class
       new Dog{}.bark
           ^

发现,方法bark尽管有具体定义,但是编译器无法创建匿名类对象,原因是“特质中的方法barkabstract override修饰,但是无法在基类中找到具体的实现”。从这一点上讲,方法bark此时也具有抽象性(abstract)。

这是符合定义的,因为显然Dog.barkabstract override修饰,且被重载方法Animal.barkincomplete ,从而 Dog.bark也是 incomplete的,自然会报错,即:

  incomplete(Dog.bark)
=abstract(Dog.bark)∨[ abstractOverride(Dog.bark)∧incomplete( overrided(Dog.bark))]
=False∨[ Trueincomplete( Animal.bark)]
=False∨[ TrueTrue]
=True

为了解决这个问题,有两种:
只要让 被重载方法Animal.bark不是 incomplete即可(incomplete( overrided(Dog.bark))==False):

abstract class Animal{
    def bark=println("Emmm....")  //Now it's not incomplete
}
trait Dog extends Animal{
    //So method bark is also not incomplete
    abstract override def bark=println("WoooW!")
}
//the invocation makes sense
new Dog{}.bark //WoooW!

或者只要让重载方法Dog.bark不被abstract override修饰即可( abstractOverride(Dog.bark)==False):

abstract class Animal{
    def bark    //abstract method
}
trait Dog extends Animal{
    override def bark=println("WoooW!")  //redundant modifier override
}
new Dog{}.bark  //WoooW!

以上只是极端情况的演示,但是实际上没有必要这么做。

结论

只有在满足下面条件的情况下,我们才应在trait 中定义的某一方法之前添加
abstract 关键字:该方法调用了super 对象中的另一个方法,但是被调用的
这个方法在该trait 的父类中尚未定义具体的实现方法。

考虑下面的样板代码:

abstract class AbstractSuper{
    def abstractMethod( Params ) : ReturnType
}

trait TraitSub extends AbstractSuper{
    def abstractMethod( Params ) :ReturnType ={  //implements the abstract method
        /**
        重载代码实现
        */
    }
}

如果重载代码实现逻辑中:

为什么abstract override只能用来修饰特质的成员呢?假设可以用来修饰抽象类的方法,那么该方法本身就应该使用了父抽象类的抽象方法super.abstractMethod,但是这在设计逻辑上是说不通的:父抽象类的抽象方法本就是交由子类来实现的,表示抽象的通用行为,子类却又在实现逻辑内部调用父抽象类的抽象方法,这是毫无道理的。在这里,super就是指代父抽象类

那么特质为什么能反过来使用父类的抽象方法呢,这似乎也在逻辑上说不通?因为特质有一个很特殊的性质:特质中的super动态绑定的,你应该注意到上面的两类情况讨论中,我并没有使用AbstractSuper.abstractMethod的写法而是写成super.abstractMethod,也就是说,尽管TraitSub继承了抽象类AbstractSuper,但是它的super并不指代父抽象类AbstractSuper,而是在运行过程中动态绑定,所以在此之前都是不定的,是抽象的。

这样的性质使得特质变得可堆叠。考虑一个抽象类A,其内有抽象方法m,有一个具体类C继承了A并实现了方法m,另外有一个特质T也继承了A,并且m的实现内引用了super.m,故被标明abstract override
现在假定有一个类P,它继承了C,设pP的实例,那么使用p.m实际就由C.m代理。现在,将特质T混入:class P extends C with T,那么使用p.m,根据特质的线性化(扁平化处理,类似python中的MRO),它将由T.m进行代理,而在其内部的super.m实际上绑定为C.m,因此p.m=>T.m ( C.m )
试想如果有很多特质,它们对方法m具有多态性,那么按照不同顺序混入,super也就绑定不同的实例,可能就展现为p.m=>T1.m ( T2.m( ....Tn.m (C.m ))... ),通过将特质堆叠,使得方法变得更有选择性和层次感。

特质的线性化顺序

线性化算法
(1) 当前实例的具体类型会被放到线性化后的首个元素位置处。
(2) 按照该实例父类型的顺序从右到左的放置节点,针对每个父类型执行线性化算法,并将执行结果合并。(我们暂且不对AnyRef 和Any 类型进行处理。)
(3) 按照从左到右的顺序,对类型节点进行检查,如果类型节点在该节点右边出现过,那么便将该类型移除。
(4) 在类型线性化层次结构末尾处添加AnyRef 和Any 类型。
如果是对值类(如Int、Short、Double等)执行线性化算法,请使用AnyVal 类型替代AnyRef 类型。

例如,有如下继承关系(伪代码):

class A
trait B (A)
trait C (A)
trait D (A)
trait E (C)
trait F (C)
class G (D,E,F,B)

对G进行线性化:

  1. 当前实例的具体类型会被放到线性化后的首个元素位置处。
    【方法链】:G
  2. 按照其父类型的顺序从右到左的放置节点
    【方法链】:G B F E D
  3. 针对每个父类型执行线性化算法
    【方法链】:
    G B F E D
    G B A F C E C D A
  4. 按照从左到右的顺序,对类型节点进行检查,如果类型节点在该节点右边出现过,那么便将该类型移除。
    【方法链】:
    G B (A) F (C) E C D A
    G B       F       E C D A

因此对G的线性化即(从左至右为):GBFECDA。方法链(super的绑定)也按照这个顺序进行。显然,根据特质不同的混入顺序,这个方法链也会不同。

上一篇下一篇

猜你喜欢

热点阅读