我的示例课:
class Thing() {
// Public vars
var one: Int = 1
var two: Int = 2
// Scala-style getter/setter
private var _age: Long = 3
def age: Long = _age
def age_=(a: Long) = _age = a
override def toString(): String = s"one: $one, two: $two, age: $age"
}
// A name -> setter holder class
case class Member(name:String, setter: MethodSymbol)
现在我的反射代码和setter调用程序:
def analyze[T](classSymbol: ClassSymbol)(implicit tt:TypeTag[T]): List[Member] = {
val tpe = tt.tpe
val classMirror = currentMirror.reflectClass(classSymbol)
tpe.members.filter(p => p.isPublic).collect {
// Ignore the non-methods and methods we don't care about
case (p) if (p.isMethod && tpe.member(TermName(p.name.toString + "_$eq")) != NoSymbol) =>
val setter =
// 1) Scala-stype getters/setters
tpe.members.filter(f => f.name.toString == p.name.toString + "_" && f.isMethod).headOption.map(_.asMethod)
// 2) Public var (no getter/setter)
.orElse(tpe.members.filter(f => f.name.toString == p.name.toString).headOption.map(_.asMethod))
Member(p.name.toString,setter.get)
}.toList
}
def setValue[T](t:T, members: List[Member], field: String, value: Any)(implicit tt:TypeTag[T]) = {
implicit val classTag = ClassTag[T](typeTag[T].mirror.runtimeClass(typeTag[T].tpe))
members.find(_.name == field).map{ m =>
println("--> Setting field "+m.name+" to "+value)
currentMirror.reflect(t).reflectMethod(m.setter)(value)
}
}
最后是我的尝试用法:
val obj = new Thing()
println("Before: "+obj)
val members = analyze[Thing](currentMirror.classSymbol(obj.getClass()))
println(members.mkString("\n"))
// Now set some value on a public var
setValue[Thing](obj, members, "one", 99)
println("After: "+obj)
输出显示:
Before: one: 1, two: 2, age: 3
Member(age,method age)
Member(two,variable two)
Member(one,variable one)
--> Setting field one to 99
After: one: 1, two: 2, age: 3
如您所见,反射正确区分了getter / setter字段和public var字段。尝试设置时,它找到了该字段,并调用了setter,没有抛出异常,但是...我的对象中实际上没有任何值更改(对于getter / setter字段或var字段都是如此)。为什么我看不到价值的变化?
答案 0 :(得分:1)
找到了它...已发布以防他人使用。
我正在过滤错误的内容。 Scala中的设置器的名称格式类似于“ foo_ $ eq”,因此我必须寻找该模式。调用这些方法时,实例类中的值将按预期更改。修改后的反射代码如下:
def analyze[T](classSymbol: ClassSymbol, isGetterSetter:Boolean = false)(implicit tt:TypeTag[T]): List[Member] = {
val tpe = tt.tpe
val Pattern = """(.*)_\$eq""".r
tpe.members.filter(p => p.isPublic).collect {
case p if (p.isMethod && p.name.toString.endsWith("_$eq")) =>
val Pattern(name) = p.name.toString
val setter =
// 1) Scala-stype getters/setters
tpe.members.filter(f => f.name.toString == p.name.toString + "_" && f.isMethod).headOption.map(_.asMethod)
// 2) Public var (no getter/setter)
.orElse(tpe.members.filter(f => f.name.toString == p.name.toString).headOption.map(_.asMethod))
Member(name,setter.get)
}.toList
}