将哈希映射键/值对映射到Scala中的命名构造函数参数

Chr*_*oop 9 scala

是否可以将Map的键值对映射到具有命名参数的Scala构造函数?

也就是说,给定

class Person(val firstname: String, val lastname: String) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

...如何使用类似的地图创建Person的实例

val args = Map("firstname" -> "John", "lastname" -> "Doe", "ignored" -> "value")
Run Code Online (Sandbox Code Playgroud)

我最终想要实现的是将Node4J Node对象映射到Scala值对象的好方法.

Dun*_*gor 11

这里的关键见解是构造函数参数名称可用的,因为它们是构造函数创建的字段的名称.因此,如果构造函数不对其参数执行任何操作,而是将它们分配给字段,那么我们可以忽略它并直接使用这些字段.

我们可以用:

def setFields[A](o : A, values: Map[String, Any]): A = {
  for ((name, value) <- values) setField(o, name, value)
  o
}

def setField(o: Any, fieldName: String, fieldValue: Any) {
  // TODO - look up the class hierarchy for superclass fields
  o.getClass.getDeclaredFields.find( _.getName == fieldName) match {
    case Some(field) => {
      field.setAccessible(true)
      field.set(o, fieldValue)
    }
    case None =>
      throw new IllegalArgumentException("No field named " + fieldName)
  }
Run Code Online (Sandbox Code Playgroud)

我们可以打电话给一个空白的人:

test("test setFields") {
  val p = setFields(new Person(null, null, -1), Map("firstname" -> "Duncan", "lastname" -> "McGregor", "age" -> 44))
  p.firstname should be ("Duncan")
  p.lastname should be ("McGregor")
  p.age should be (44)
}
Run Code Online (Sandbox Code Playgroud)

当然,我们可以通过一点拉皮条来做得更好:

implicit def any2WithFields[A](o: A) = new AnyRef {
  def withFields(values: Map[String, Any]): A = setFields(o, values)
  def withFields(values: Pair[String, Any]*): A = withFields(Map(values :_*))
}
Run Code Online (Sandbox Code Playgroud)

这样你就可以打电话:

new Person(null, null, -1).withFields("firstname" -> "Duncan", "lastname" -> "McGregor", "age" -> 44)
Run Code Online (Sandbox Code Playgroud)

如果必须调用构造函数很烦人,Objenesis允许您忽略缺少无参数构造函数:

val objensis = new ObjenesisStd 

def create[A](implicit m: scala.reflect.Manifest[A]): A = 
  objensis.newInstance(m.erasure).asInstanceOf[A]
Run Code Online (Sandbox Code Playgroud)

现在我们可以把两者结合起来写

create[Person].withFields("firstname" -> "Duncan", "lastname" -> "McGregor", "age" -> 44)
Run Code Online (Sandbox Code Playgroud)