【问题标题】:Scala case class copy with dynamic named parameter具有动态命名参数的Scala案例类副本
【发布时间】:2013-06-26 05:35:25
【问题描述】:

对于具有参数数量的scala案例类(21!!)

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

还有一个复制方法允许使用命名参数覆盖:Car.copy(brand = Kia) 哪里会变成 type = jeep, brand = Kia, door = 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 个参数的案例类时都重复 21 个参数分配。

非常感谢!

【问题讨论】:

  • 我不明白为什么 copy 命令还没有完全满足您的需求。为什么要动态提供参数?
  • @RexKerr 我可以看到:他得到了一个(名称,值)对的映射,然后想用它调用Car.copy()。您的方式会将其全部硬编码为特定参数。

标签: scala


【解决方案1】:

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]
    }
  }

【讨论】:

【解决方案2】:

不可能使用案例类。

复制在编译时生成的方法和在编译时处理的命名参数。在运行时不可能做到这一点。

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

【讨论】:

    【解决方案3】:

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

    这有点复杂,因为copy 是一种合成方法,您必须为除要替换的字段之外的所有字段调用 getter。

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

    【讨论】:

      【解决方案4】:

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

      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, ...)
      

      您似乎了解这两个功能,但觉得它们不符合您的需要 - 您能解释一下原因吗?

      【讨论】:

      • 我认为 OP 的问题是有效的。在您的示例中,“类型”和“门”是硬编码的。如果您只知道在运行时会得到什么键值对呢?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-17
      • 2011-11-09
      • 2022-01-27
      相关资源
      最近更新 更多