Scala:Trait Mixin与抽象基类

sie*_*ent 7 scala traits mixins abstract-base-class

我有一个抽象的基类(Base),它有一些为它定义的堆栈特征(StackingTrait).

trait Base {
  def foo
}
trait StackingTrait extends Base {
  abstract override def foo { super.foo }
}
Run Code Online (Sandbox Code Playgroud)

使用以下语法实现子类非常方便,但是这不起作用,因为编译器说foo需要声明,override然后abstract override重新编译,这是一个无效的因为Impl是一个类.

class Impl extends Base with StackingTrait {
  def foo {}
}
Run Code Online (Sandbox Code Playgroud)

我想不出为什么不允许这样的语法的一个很好的理由; foo在逻辑上被定义为Impl使得在概念上发生堆叠的顺序保持不变.

注意:我发现这种解决方法可以有效地完成我想要的相同操作,但是帮助类的必要性使我想要一个更好的解决方案.

class ImplHelper extends Base {
  def foo {}
}
class Impl extends ImplHelper with StackingTrait
Run Code Online (Sandbox Code Playgroud)

为什么没有编译所需的语法,是否有一个优雅的解决方案?

Rég*_*les 4

我的理解是,虽然错误消息可能令人困惑,但行为是正确的。 foo被声明为abstract overridein StackingTrait,因此在任何混合的具体类中StackingTrait必须有beforeabstract的具体(未标记为 )实现(相对于线性化顺序)。这是因为指的是线性化顺序中之前的特征,因此肯定需要混入before的具体实现,否则将是无意义的。foo StackingTraitsuperfooStackingTraitsuper.foo

当你这样做时:

class Impl extends Base with StackingTrait {
  def foo {}
}
Run Code Online (Sandbox Code Playgroud)

线性化顺序为Base<- StackingTrait<- Impl。之前唯一的特征StackingTraitBase并且Base没有定义 的具体实现foo

但是当你这样做时:

traitImplHelper extends Base {
  def foo {}
}
class Impl extends ImplHelper with StackingTrait
Run Code Online (Sandbox Code Playgroud)

线性化顺序变为:Base<- ImplHelper<- StackingTrait<-Impl 这里ImplHelper包含 的具体定义foo,且肯定位于之前 StackingTrait

值得一提的是,如果您在ImplHelper之后进行了混合StackingTrait(如 中所示class Impl extends StackingTrait with ImplHelper),您将再次遇到相同的问题并且编译失败。

所以,这对我来说看起来相当一致。我不知道有什么方法可以让它按照您的预期进行编译。但是,如果您更关心使其更易于编写Impl(并且能够foo在此处定义而不需要单独的类/特征)而不是使其易于编写Baseor 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 {}
}
Run Code Online (Sandbox Code Playgroud)

就像在原始版本中一样,您强制每个具体类实现foo(以 的形式fooImpl),这次它确实可以编译。这里的缺点是 whilefooImpl不能调用super.foo(它没有意义并且会进入无限循环),编译器不会警告您。