我有一个抽象基类(Base
),它为它(StackingTrait
)定义了一些堆叠特征。
trait Base {
def foo
}
trait StackingTrait extends Base {
abstract override def foo { super.foo }
}
使用以下语法实现子类将非常方便,但这不起作用,因为编译器说foo需要用override
声明,然后用abstract override
重新编译,这是无效的,因为Impl
是一个类。
class Impl extends Base with StackingTrait {
def foo {}
}
我想不出一个好的理由为什么这样的语法不被允许;foo在逻辑上使用Impl
定义,因此在概念上发生堆叠的顺序保持不变。
注意:我找到了这个解决方法,它将有效地完成我想要的相同的事情,但是helper类的必要性使我想要一个更好的解决方案。
class ImplHelper extends Base {
def foo {}
}
class Impl extends ImplHelper with StackingTrait
为什么期望的语法不能编译,是否有一个优雅的解决方案?
我的理解是,虽然错误消息可能令人困惑,但行为是正确的。foo
在StackingTrait
中被声明为abstract override
,因此在任何混合StackingTrait
的具体类中,必须在 StackingTrait
之前有一个foo
的具体实现(未标记为abstract
)(相对于线性化顺序)。这是因为super
在线性化顺序中引用了之前的特性,所以在StackingTrait
混合之前肯定需要foo
的具体实现,否则super.foo
将是无意义的。
当你这样做的时候:
class Impl extends Base with StackingTrait {
def foo {}
}
线性化顺序为Base
<- StackingTrait
<- Impl
。StackingTrait
之前唯一的trait是Base
, Base
没有定义foo
的具体实现。
但是当你这样做的时候:
traitImplHelper extends Base {
def foo {}
}
class Impl extends ImplHelper with StackingTrait
线性化顺序为:Base
<- ImplHelper
<- StackingTrait
<- Impl
这里ImplHelper
包含了foo
的具体定义,并且肯定在 StackingTrait
之前。
值得注意的是,如果您在StackingTrait
之后混合了ImplHelper
(如class Impl extends StackingTrait with ImplHelper
),您将再次遇到相同的问题,并且它将无法编译。
所以,在我看来这是相当一致的。我不知道有什么方法可以使它像你想的那样编译。然而,如果你更关心如何更容易地编写Impl
(并能够在那里定义foo
,而不需要单独的类/trait),而不是编写Base
或StackingTrait
,你仍然可以这样做:
trait Base {
protected def fooImpl
def foo { fooImpl }
}
trait StackingTrait extends Base {
abstract override def foo { super.foo }
}
class Impl extends Base with StackingTrait {
protected def fooImpl {}
}
就像在原始版本中一样,你强制每个具体类实现foo
(以fooImpl
的形式),这次它确实编译了。这里的缺点是,虽然fooImpl
不能调用super.foo
(这没有意义,会进入一个无限循环),但编译器不会警告您。
您可以尝试使用示例中提到的self类型来代替扩展trait。https://docs.scala-lang.org/tour/self-types.html。