具有动态命名参数的Scala案例类副本

时间:2013-06-26 05:35:26

标签: scala

对于包含参数数量的scala案例类(21 !!)

e.g。 case class Car(type: String, brand: String, door: Int ....) 其中type = jeep,brand = toyota,door = 4 .... etc

还有一种复制方法允许使用命名参数覆盖:Car.copy(brand = Kia) 哪里会变成type = jeep,brand =起亚,门= 2 ......等等。

我的问题是,无论如何我可以动态提供命名参数吗?

def copyCar(key: String, name: String) = {
  Car.copy("key" = "name") // this is something I make up and want to see if would work
}

scala反射库可以在这里提供帮助吗?

我使用复制方法的原因是每次创建只更改了1或2个参数的case类时,我不想重复21个参数赋值。

非常感谢!

4 个答案:

答案 0 :(得分:3)

FWIW,我刚刚实现了Java反射版本:CaseClassCopy.scala。我尝试了TypeTag version,但它并没有用; TypeTag对此目的的限制太多了。

  def copy(o: AnyRef, vals: (String, Any)*) = {
    val copier = new Copier(o.getClass)
    copier(o, vals: _*)
  }

  /**
   * Utility class for providing copying of a designated case class with minimal overhead.
   */
  class Copier(cls: Class[_]) {
    private val ctor = cls.getConstructors.apply(0)
    private val getters = cls.getDeclaredFields
      .filter {
      f =>
        val m = f.getModifiers
        Modifier.isPrivate(m) && Modifier.isFinal(m) && !Modifier.isStatic(m)
    }
      .take(ctor.getParameterTypes.size)
      .map(f => cls.getMethod(f.getName))

    /**
     * A reflective, non-generic version of case class copying.
     */
    def apply[T](o: T, vals: (String, Any)*): T = {
      val byIx = vals.map {
        case (name, value) =>
          val ix = getters.indexWhere(_.getName == name)
          if (ix < 0) throw new IllegalArgumentException("Unknown field: " + name)
          (ix, value.asInstanceOf[Object])
      }.toMap

      val args = (0 until getters.size).map {
        i =>
          byIx.get(i)
            .getOrElse(getters(i).invoke(o))
      }
      ctor.newInstance(args: _*).asInstanceOf[T]
    }
  }

答案 1 :(得分:0)

使用案例类是不可能的。

编译时生成的复制方法和编译时处理的命名参数。没有可能在运行时进行。

Dynamic可能有助于解决您的问题:http://hacking-scala.tumblr.com/post/49051516694/introduction-to-type-dynamic

答案 2 :(得分:0)

是的,你需要使用反射来做到这一点。

有点涉及,因为copy是一种合成方法,你必须为除了你想要替换的那个字段之外的所有字段调用getter。

为了给你一个想法,copy method in this class正是这样做的,除了使用参数索引而不是名称。它调用伴侣对象的apply方法,但效果是一样的。

答案 3 :(得分:0)

我有点困惑 - 以下不是你需要的是什么?

car: Car = ...          // Retrieve an instance of Car somehow.
car.copy(type = "jeep") // Copied instance, only the type has been changed.
car.copy(door = 4)      // Copied instance, only the number of doors has changed.
// ...

是否因为你有很多参数用于初始实例创建?在这种情况下,您可以不使用默认值吗?

case class Car(type: String = "Jeep", door: Int = 4, ...)

您似乎了解这些功能,并认为它们不符合您的需求 - 您能解释一下原因吗?