这里的关键见解是构造函数参数名称是可用的,因为它们是构造函数创建的字段的名称。所以只要构造函数对它的参数不做任何事情,而是将它们分配给字段,那么我们可以忽略它并直接使用字段。
我们可以使用:
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)
}
我们可以召唤一个空白的人:
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)
}
当然,我们可以通过拉皮条做得更好:
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 :_*))
}
这样你就可以打电话了:
new Person(null, null, -1).withFields("firstname" -> "Duncan", "lastname" -> "McGregor", "age" -> 44)
如果不得不调用构造函数很烦人,Objenesis 可以让您忽略缺少无参数构造函数:
val objensis = new ObjenesisStd
def create[A](implicit m: scala.reflect.Manifest[A]): A =
objensis.newInstance(m.erasure).asInstanceOf[A]
现在我们可以把两者结合起来写
create[Person].withFields("firstname" -> "Duncan", "lastname" -> "McGregor", "age" -> 44)