我有一个抽象基类(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
,因此在概念上对堆叠的排序保持不变。
注意: 我想出了这个可以有效地做同样事情的解决方法,但是帮助类的必要性让我想要一个更好的解决方案。
class ImplHelper extends Base {
def foo {}
}
class Impl extends ImplHelper with StackingTrait
为什么所需的语法无法编译并且是否有优雅的解决方案?
答案 0 :(得分:4)
我的理解是,虽然错误消息可能令人困惑,但行为是正确的。
foo
中的abstract override
被声明为StackingTrait
,因此在混合StackingTrait
的任何具体类中,必须有一个具体的(未标记为abstract
)实现<{1}} 之前 foo
(相对于线性化顺序)。这是因为StackingTrait
在线性化顺序中引用了之前的特征,因此在混合super
之前肯定需要foo
的具体实现,或StackingTrait
将是荒谬的。
执行此操作时:
super.foo
线性化顺序为class Impl extends Base with StackingTrait {
def foo {}
}
&lt; - Base
&lt; - StackingTrait
。 Impl
StackingTrait
和Base
之前的唯一特征没有定义Base
的具体实现。
但是当你这样做时:
foo
线性化顺序变为:traitImplHelper extends Base {
def foo {}
}
class Impl extends ImplHelper with StackingTrait
&lt; - Base
&lt; - ImplHelper
&lt; - StackingTrait
此处Impl
包含ImplHelper
的具体定义,并且在 foo
之前绝对是。
值得一提的是,如果您在StackingTrait
之后混合ImplHelper
(如在StackingTrait
中),那么您将再次遇到同样的问题并且无法编译。
所以,这看起来与我相当一致。
我不知道如何按照您的意图进行编译。但是,如果您更关心的是更容易编写class Impl extends StackingTrait with ImplHelper
(并且能够在那里定义Impl
而不需要单独的类/特征),而不是简单地编写foo
}或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
(它没有意义并且会进入无限循环),编译器不会警告你。
答案 1 :(得分:0)
您可以尝试使用自类型,而不是扩展特征,如示例中所述。 https://docs.scala-lang.org/tour/self-types.html。