8

我有一个抽象的 Scala 类Base,它有子类Derived1Derived2. Base定义了一个函数 f(),它返回一个与其实现类相同类型的对象。所以Derived1.f()返回Derived1Derived2.f()返回Derived2。我如何在 Scala 中编写这个?

到目前为止,这是我想出的。

package com.github.wpm.cancan

abstract class Base {
  def f[C <: Base]: C
}

case class Derived1(x: Int) extends Base {
  def f[Derived1] = Derived1(x + 1)
}

case class Derived2(x: Int) extends Base {
  def f[Derived2] = Derived2(x + 2)
}

这会产生以下编译器错误:

type mismatch;
[error]  found   : com.github.wpm.cancan.Derived1
[error]  required: Derived1
[error]   def f[Derived1] = Derived1(x + 1)

type mismatch;
[error]  found   : com.github.wpm.cancan.Derived2
[error]  required: Derived2
[error]   def f[Derived2] = Derived2(x + 2)

这个错误信息让我很困惑,因为我认为com.github.wpm.cancan.Derived1应该和Derived1这个上下文中的一样。

4

2 回答 2

15

Randall Schulz 指出了您当前代码不起作用的原因之一。不过,使用F有界多态性可以得到你想要的:

trait Base[C <: Base[C]] { def f: C }

case class Derived1(x: Int) extends Base[Derived1] {
  def f: Derived1 = Derived1(x + 1)
}

case class Derived2(x: Int) extends Base[Derived2] {
  // Note that you don't have to provide the return type here.
  def f = Derived2(x + 2)
}

基本特征上的类型参数允许您在那里讨论实现类——例如在返回类型中为f.

于 2013-02-07T00:29:11.863 回答
8

C只是为了增加一个关于(非常好)Travis Brown 回答的小精确度:这不是trait Base[C <: Base[C]]让您参考实现类;它只是坚持subclass extends Base[subclass]让你这样做的写作惯例。据我所知,没有办法指代这种类型。为了澄清我的意思,这编译

trait Base[C <: Base[C]] { def f: C }

case class Derived1(x: Int) extends Base[Derived1] {
  def f: Derived1 = Derived1(x + 1)
}
// a Derived2 where f returns Derived1!!
case class Derived2(x: Int) extends Base[Derived1] {
  def f = Derived1(x + 2)
}

现在,如果您将拥有的所有实现Base都是案例类,那么您可以通过自类型绑定来实现这一点:

trait Base[C <: Base[C]] { self: C => 
  def f: C 
}

case class Derived1(x: Int) extends Base[Derived1] {
  def f: Derived1 = Derived1(x + 1)
}
// a Derived2 where f returns Derived1!!
// this won't compile now
case class Derived2(x: Int) extends Base[Derived1] {
  def f = Derived1(x + 2)
}
于 2013-02-07T15:53:19.133 回答