我可以使用动态参数调用case类复制方法而无需显式反射吗?

时间:2018-04-10 14:53:05

标签: scala reflection case-class

问题是我有一些具有不同字段的独立案例类。 我想编写一个方法,返回给定类型的case类的实例,其中一个参数已更改。

def updateParam[C : ClassTag](c: C, paramName: String, paramValue: Any): C = ...

我利用this answer,但它涉及使用不那么美丽的反射机制。 是否有可能避免使用反射,或者至少隐藏在某些库的引擎盖下?

1 个答案:

答案 0 :(得分:1)

如果要在不同类的实例上调用相同的函数,则它是多态

如果这些类不相关,则多态性为 ad-hoc

在Scala中实现ad-hoc多态的常用方法是使用类型类。在这里,您可以实现类型类Copyable

trait Copyable[C] {
  def copy(c: C, paramName: String, value: Any): C
}

并按如下方式实施您的方法:

def updateParam[C: Copyable](c: C, paramName: String, paramValue: Any): C = {
  implicitly[Copyable[C]].copy(c, paramName, paramValue)
}

现在,如果您有两个不相关的类,尽管您希望在相同的上下文中使用它们,但它们在某种程度上最终无关:

case class Foo(paramName: String, foo: Int)
case class Bar(paramName: String, bar: Option[Double])

您可以提供使其成为Copyable的类型类:

implicit object FooIsCopyable extends Copyable[Foo] {
  def copy(f: Foo, paramName: String, value: Any): Foo = {
    if (paramName == "paramName") {
      f.copy(paramName = value.asInstanceOf[String])
    } else if (paramName == "foo") {
      f.copy(foo = value.asInstanceOf[Int])
    } else {
      throw new IllegalArgumentException("Foo has no param `" + paramName + "`")
    }
  }
}

implicit object BarIsCopyable extends Copyable[Bar] {
  def copy(b: Bar, paramName: String, value: Any): Bar = {
    if (paramName == "paramName") {
      b.copy(paramName = value.asInstanceOf[String])
    } else if (paramName == "bar") {
      b.copy(bar = value.asInstanceOf[Option[Double]])
    } else {
      throw new IllegalArgumentException("Bar has no param `" + paramName + "`")
    }
  }
}

然后像这样使用你的方法:

val f = Foo("hello", 42)
val b = Bar("bye", Some(58.0))

println(updateParam(f, "paramName", "hello, world"))
println(updateParam(b, "paramName", "obey"))

然后打印:

Foo(hello, world,42)
Bar(obey,Some(58.0))

但是,当你可以这样做的时候,你想要的并不完全清楚。 FooBar在同样的情况下最终是如何发生的,所以最终基本上是鸭子打字,需要方法copy?也许你应该首先消除根本原因。

您可能还想看一下镜头