【问题标题】:How to force subclasses to override an inherited method with a more specific return type如何强制子类使用更具体的返回类型覆盖继承的方法
【发布时间】:2018-02-16 12:40:49
【问题描述】:

考虑这个例子:

trait SpecialIntIterable extends Iterable[Int] {

  override def iterator: Iterator[Int] = ??? // implementation not relevant

  override def tail : SpecialIntIterable

  // plus some more methods...

}

class MyPrimeIterable extends SpecialIntIterable {

  override def tail : SpecialIntIterable = ??? // implementation not relevant

}

我正在尝试强制SpecialIntIterable 的所有子类实现tail,因此它再次返回SpecialIntIterable。这可能吗?

目前我收到以下编译器错误:

overriding method tail in trait SpecialIntIterable of type => de.cointrade.trader.SpecialIntIterable;     
method tail in trait TraversableLike of type => Iterable[Int] has incompatible type;
(Note that method tail in trait SpecialIntIterable of type => de.cointrade.trader.SpecialIntIterable is abstract,
and is therefore overridden by concrete method tail in trait TraversableLike of type => Iterable[Int])
trait SpecialIntIterable extends Iterable[Int] {

编辑: 我发现的一个解决方案是在SpecialIntIterable 中引入一个抽象辅助方法(例如称为rest)并让tail 委托给它。但是,我想知道如果没有额外的成员是否有可能。

【问题讨论】:

  • ...也许我误解了这个问题,因为我不明白“比 what 更具体?”
  • @AndreyTyukin,我的意思比Iterable[Int] 更具体。

标签: scala inheritance overriding


【解决方案1】:

Architecture of Scala Collections 中,这被称为“相同结果类型原则”。在集合库中,这是通过名称以Like 结尾的模板类来实现的,偶尔会混合一些 F 绑定多态性。本质上,如果你想要一个看起来像这样的界面

trait Foo {
  def makeResult: Foo // how enforce same-result-type ?
}

但您想强制所有实现子类返回它们自己类型的东西,而不是 Foo,您修改 Foo 如下:

trait FooLike[+Repr] {
  def makeResult: Repr
}

然后实现子类为

class Bar extends FooLike[Bar] {
  def makeResult: Bar = ??? // now `Bar` is enforced as return type
}

在你的情况下,你可以尝试这样的事情:

import scala.collection.IterableLike
import scala.collection.mutable.Builder

trait SpecialIntIterableLike[+Repr] extends IterableLike[Int, Repr]

abstract class SpecialIntIterable 
  extends Iterable[Int] 
  with SpecialIntIterableLike[SpecialIntIterable] {

  override def newBuilder: Builder[Int, SpecialIntIterable] = ???
}

class MyPrimeIterable extends SpecialIntIterable with SpecialIntIterableLike[MyPrimeIterable] {
  def iterator: Iterator[Int] = ???
  override def tail : MyPrimeIterable = ???                    // must override, otherwise compile-time errors
  override def newBuilder: Builder[Int, MyPrimeIterable] = ??? // must override, otherwise compile-time errors
}

注意newBuilder-方法必须被覆盖以解决冲突(这在上面的链接文档中提到)。


编辑

即使您的MyPrimeIterable 选择不扩展SpecialIntIterableLike,它仍然必须实现一个至少返回SpecialIntIterabletail

class MyOtherIterable extends SpecialIntIterable {
  def iterator: Iterator[Int] = ???
  override def newBuilder: Builder[Int, SpecialIntIterable] = ???
  // def tail: Iterable[Int] = List(0) // won't compile
}

如果我们尝试放宽tail 的返回类型,它将无法编译,因为SpecialIntIterable 扩展了SpecialIntIterableLike[SpecialIntIterable]

这类似于扩展例如IndexedSeq[X]:您可以选择不实现IndexedSeqLike[X, MyType],因此“same-result-type”-property会稍微恶化,但tail的结果类型会至少仍然是 IndexedSeq[X],而不是 Iterable[X] 或比 IndexedSeq[X] 更弱的东西。

【讨论】:

  • 感谢您的回答!但是,它似乎并没有真正解决我的问题。 MyPrimeIterable 仍然可以决定不实现 tail 或使用更通用的返回类型来实现它。顺便说一句,我不要求返回类型相同(即MyPrimeIterable),只有SpecialIntIterable 是抽象的。
  • 在上面的例子中,MyPrimeIterable不可能实现不是SpecialIntIterabletail。它可以放弃SpecialIntIterable[MyPrimeIterable],但不能放弃tail 必须至少返回一个SpecialIntIterable 的要求。还是我错过了什么?更新了我的答案以专门解决 tail 返回类型的界限。
  • 我想我现在明白了:如果MyOtherIterable 根本没有覆盖tail,它将自动获得正确的类型SpecialIntIterable。我很困惑,因为我可以评论 def tail... 行并且它仍然编译。
  • 那么没有直接的方法来“取消定义”超类的方法来强制子类重新实现它吗?或者至少强制他们使用比超级实现的返回类型更具体的返回类型来实现它?
  • @lex82 啊,所以你想通过扩展特征中的抽象实现来覆盖超特征中的具体实现?不幸的是,那是not possible。我认为这与如何在线性化特征链中解决正确的方法实现有关:它只是自下而上地遍历线性化链,并选择第一个实现的方法。似乎没有办法“抽象地重新映射”已经实现的方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-19
  • 2012-11-20
  • 1970-01-01
  • 2011-05-21
  • 2017-11-18
  • 2019-12-09
  • 1970-01-01
相关资源
最近更新 更多