5

我有两个从抽象基类继承的案例类。我想在抽象基类上定义一些方法,这些方法使用继承案例类上的复制方法(因此返回子类的实例。)有没有办法使用自我类型来做到这一点?

示例代码:

abstract class BaseClass(a: String, b: Int) {
  this: case class => //not legal, but I'm looking for something similar

  def doubleB(newB: Int) = this.copy(b = b * 2) //doesn't work because BaseClass has no copy
}

case class HasC(a: String, b: Int, c: Boolean) extends BaseClass(a, b) {
  def doesStuffWithC(newC: Boolean) = {
    ...
  }
}

case class HasD(a: String, b: Int, D: Double) extends BaseClass(a, b) {
  def doesStuffWithD(newD: Double) = {
    ...
  }
}

由于这个问题,我已经弄清楚了如何获得我想要的结果: How to use Scala's this typing, abstract types, etc. to implement a Self type? 但它涉及向 BaseClass 添加一个 makeCopy 方法,并在每个子案例类中调用 copy 来覆盖它,并且语法(尤其是对于 Self 类型)相当混乱。有没有办法用 Scala 的内置自键入来做到这一点?

4

3 回答 3

5

你不能做你想做的,因为copy 需要知道所有可能的参数。因此,即使案例类继承自Copyable,它也不是copy您需要的。此外,如果您要保持类型直接,那么您会因 Scala 缺少“ MyType”而受阻。所以你不能扩展一个基类。但是,您可以添加抽象方法和类型注释:

abstract class BaseClass[C <: BaseClass[_]](a: String, b: Int) {
  def setB(b0: Int): C
  def doubleB(b0: Int) = setB(b0*2)
}
case class HasC(a: String, b: Int, c: Boolean) extends BaseClass[HasC](a,b) {
  def setB(b0: Int) = this.copy(b = b0)
  def doesStuffWithC(c0: Boolean) = doubleB(if (c0) b else -b).copy(c = c0)
}

然后你可以:

scala> HasC("fish",1,false).doesStuffWithC(true)
res47: HasC = HasC(fish,2,true)

如果您有很多依赖于复制能力的共享功能b(许多方法或少量复杂方法),那么这项额外的工作将是值得的——也就是说,这解决了 DRY 问题。相反,如果您想抽象HasC其他派生类,您可以使用BaseClass[_]或添加另一个级别来定义setB(b0: Int): BaseBase或干脆忘记类型参数化并BaseClass用作返回类型(但认识到HasC不能使用BaseClass方法并仍保留其类型标识)。

于 2012-04-24T20:24:39.583 回答
1

您可以在抽象类中定义一个 makeCopy,该类接受一个接受 Unit 并返回 BaseClass 的复制器函数,然后在使用它的方法中(如 doubleB)在案例类主体中覆盖它们并通过传递它来使用 makeCopy匿名函数创建一个新副本的工作,道具发生了变化,如下所示:

package delegatedcopy

abstract class BaseClass(a: String, b:Int){
  def aField = a
  def bField = b
  def doubleB:BaseClass
  def makeCopy(copier: () => BaseClass):BaseClass = copier()
}

case class HasC(override val aField: String, override val bField: Int, cField: Boolean) extends BaseClass(aField, bField){
  override def doubleB:BaseClass = makeCopy( ()=> HasC(aField, bField * 2, cField) )
}

case class HasD(override val aField: String, override val bField: Int, dField:Double) extends BaseClass(aField, bField){
  override def doubleB:BaseClass = makeCopy( ()=> HasD(aField, bField * 2, dField) )
}

演示它的测试应用程序:

import delegatedcopy._

object TestApp extends Application{
  val hasC = HasC( "A C object", 5, true)
  val hasD = HasD( "A D object", 2, 3.55)
  val hasCDoubleB = hasC.doubleB
  val hasDDoubleB = hasD.doubleB

  println(hasC) // prints HasC(A C object,5,true)
  println(hasCDoubleB) //prints HasC(A C object,10,true)

  println( hasD ) // prints HasD(A D object,2,3.55)
  println( hasDDoubleB ) // prints HasD(A D object,4,3.55)
}

通过这种方式,您可以使所有子类的 makeCopy 方法与基类中的方法相同,并且可以在基类和案例类中实现或混合相当多的功能,同时将通用代码保存在安全的地方并能够在特定案例类上向客户传递 BaseClass 和模式匹配。

于 2012-04-25T13:36:47.430 回答
1

我觉得你运气不好。copy方法 onHasC和具有不同的HasD签名。由于默认参数,它有点隐藏,但基本上定义BaseClass不知道copy要调用哪个方法。

于 2012-04-24T20:17:27.630 回答