是否可以将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)
| 归档时间: |
|
| 查看次数: |
4953 次 |
| 最近记录: |