2

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

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,我不认为这是重复的,就像 a 与 aTrait不同一样Abstract Class。这个问题的答案并不让我满意,见下文。

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

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

4

3 回答 3

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}"
}
于 2012-12-10T07:55:37.523 回答
1

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

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)
}
于 2012-12-09T22:57:09.480 回答
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)
于 2012-12-09T17:59:56.127 回答