7

我有一个抽象基类 ( 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

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

4

2 回答 2

4

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

当你这样做时:

class Impl extends Base with StackingTrait {
  def foo {}
}

线性化顺序是Base<- StackingTrait<- Impl。之前唯一的特征StackingTraitBase并且Base没有定义foo.

但是当你这样做时:

traitImplHelper extends Base {
  def foo {}
}
class Impl extends ImplHelper with StackingTrait

线性化顺序变为: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 {}
}

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

于 2013-01-05T11:09:03.927 回答
0

您可以尝试使用示例中提到的自我类型,而不是扩展特征。 https://docs.scala-lang.org/tour/self-types.html

于 2020-04-16T17:52:37.513 回答