Scala:abstract override

定义

官方文档对于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.

这里面有三个要点:

  • 修饰符abstract override只能用于特质(Trait)的成员

  • 称类成员M是不完整的(incomplete),如果M是抽象的(如M只有声明没有定义),或者M被abstract override修饰,同时任何被M重载的成员也同样是不完整的。
    数学定义:令

    • overrided(x) 为被x重载的成员
    • 布尔函数 abstract(x)为x是抽象的(即只有定义)
    • 布尔函数 abstractOverride(x)为x是被abstract override
    • 布尔函数 incomplete(x)为x是不完整的,


    incomplete(M)=abstract(M)∨[ abstractOverride(M)∧incomplete( overrided(M))]

  • abstract override并不影响成员是具体的还是抽象的。如果成员只有声明则它就是抽象的,如果有具体定义,那它就是具体的。

注意到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中没有抽象成员,所以花括号为空。

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

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.

要点:

  • abstract只能修饰类,成员不能使用abstract
  • 特质没有必要声明abstract
  • 类中如果有不完整(incomplete)成员,则必须声明abstract
  • 抽象类不能实例化,除非被混入,且/或 被使用匿名类实现
  • 只有抽象类或特质才能有抽象成员

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).

要点:

  • 如果被重载的方法是父类中有具体定义的方法,则override是必须的,否则可省略(例如,实现特质的抽象方法,就没有必要写override
  • 如果用override修饰,则必然存在一个被重载成员的定义或声明,无论是抽象还是定义

说明

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
        /**
        重载代码实现
        */
    }
}

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

  • 不含有父类的 incomplete 方法:即没有super.method,或逻辑中有super.method但是super.method不是 incomplete 方法,则直接实现即可,不必加override修饰符,并且可以使用new TraitSub{}.abstractMethod来引用。
    注意
    • 这里的super.method指代父类中的任何 incomplete 方法,比如super.abstractMethod或是其他什么的。
    • 所谓“父类具有 incomplete 方法”,要么该方法就是抽象的(没有具体定义),要么其有定义,但是在定义内部包含了祖先类的 incomplete 方法,这是一个递归过程。
  • 含有父类的 incomplete 方法TraitSub.abstractMethod有具体定义,但是具体定义中又有类似super.abstractMethodincomplete方法,这样TraitSub.abstractMethod方法本身就不具体,此时必须使用abstract override来修饰,以此提醒编译器(和读者):尽管TraitSub.abstractMethod提供了方法体,但方法并没有完全实现

为什么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的绑定)也按照这个顺序进行。显然,根据特质不同的混入顺序,这个方法链也会不同。

你可能感兴趣的:(Scala:abstract override)