如何复制trait中声明的实例和覆盖值字段

时间:2012-12-09 16:25:32

标签: scala

假设我在特征中定义了一些抽象值字段:

trait Base {
  val toBeOverride: String
}

case class Impl(other:Int) extends Base {
  override val toBeOverride = "some value"
}

如何编写一个函数,我可以轻松获取克隆实例,只覆盖toBeOverride值,如下所示:

// copy only available to case class instance
// v does not have method 'copy'
def overrideBaseValue[T <: Base](v: Base) = 
    v.copy(toBeOverride = "prefix" + v.toBeOverride) 

修改

@ som-snytt,我不认为这是重复的,就像TraitAbstract Class不同。这个问题的答案不满足我,见下文。

@Blaisorblade,是的,这是一个问题。对于每个子案例类的实例,toBeOverride字段是相同的,因此它不应出现在构造函数中。

现在所有的建议都是在每个(!)子案例类中定义一个自定义的copy方法,在我看来这个方法很难看,并且显示了该语言的无能力。

3 个答案:

答案 0 :(得分:1)

最简单的解决方案是只需添加您想要的方法:

trait Base {
  val toBeOverride: String
  def copyBase(newToBeOverridden: String): Base
}

case class Impl(other:Int, override val toBeOverride: String = "some value") extends Base {
  def copyBase(newToBeOverridden: String) = copy(toBeOverride = newToBeOverridden)
}

这也允许在指定Impl的值时直接创建toBeOverride的实例(这是不可能的)。唯一的缺点是现在使用Impl的模式匹配必须更改语法 - 如果这是一个问题,请更新您的问题并添加注释。

顺便说一句,如果您只想添加前缀(如您的示例所示),那就没问题了:

case class Impl(other:Int, override val toBeOverride: String = "some value") extends Base {
  def copyBase(newToBeOverridden: String) = copy(toBeOverride = toBeOverride + newToBeOverridden)
}

答案 1 :(得分:1)

这是两种机制。

显然,在不久的将来,你将能够编写一个可以发出匿名子类的宏,但在那之前,我认为这个类型类并不是很艰难。

在这里开动动态轮胎。

import scala.language.dynamics
import scala.reflect._
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.universe._

trait Base {
  def m: String
}
case class Impl(p: Int) extends Base {
  override val m = "some value"
}

trait Basic extends Dynamic {
  protected def m: String
  def selectDynamic(f: String): Any =
    if ("m" == f) m else reflecting(this, f)
  protected def reflecting(b: Basic, f: String) = {
    val im = cm.reflect(b)
    val member = im.symbol.typeSignature member newTermName(f)
    require(member != NoSymbol, s"No such member $f")
    (im reflectMethod member.asMethod)()
  }
}
case class Implic(p: Int) extends Basic {
  override protected val m = "some value"
}

object Test extends App {

  implicit class Copy[A <: Base](val b: A) {
    def overriding(overm: String): A = (b match {
      case impl: Impl => new Impl(impl.p) { override val m = overm }
      case b: Base    => new Base { override val m = overm }
    }).asInstanceOf[A]
  }
  implicit class Proxy[A <: Basic : ClassTag](val b: A) {
    def proximately(overm: String): Basic = new Basic {
      override val m = overm
      override def selectDynamic(f: String): Any =
        if ("m" == f) overm else reflecting(b, f)
      override def toString = b.toString
    }
  }

  // asked for this
  //def overriding[T <: Base](v: Base) = v.copy(m = "prefix" + v.m)

  /* want something like this
  def overriding[T <: Base](v: Base) = new Impl(v.p) {
    override val m = "some value"
  } */

  val a = Impl(5)
  val b = a overriding "bee good"
  Console println s"$a with ${a.m} ~> $b with ${b.m}"
  // or
  val c = Implic(7)
  val d = c proximately "dynomite"
  Console println s"$c with ${c.m} ~> $d with ${d.m}"
}

答案 2 :(得分:0)

由于特征不会自动获得复制方法,您可以尝试使用Base案例类:

  case class Base(toBeOverride: String)

  case class Impl(other: Int, someVal: String = "some value") extends Base(someVal)

  def overrideBaseValue[T <: Base](v: Base) =
      v.copy(toBeOverride = "prefix" + v.toBeOverride)

您遇到的问题是,copy会返回Base的实例,我认为您无法将其转换回原来的Impl }类。例如,这不会编译:

  def overrideBaseValue[T <: Base](v: T): T =
      v.copy(toBeOverride = "prefix" + v.toBeOverride)