代码之家  ›  专栏  ›  技术社区  ›  Greg

为什么反射的setter不改变对象的字段值?

  •  0
  • Greg  · 技术社区  · 6 年前

    我的示例类:

    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字段和公共var字段。在尝试设置时,它找到了字段并调用setter,但没有抛出异常,但是…对象中没有实际更改的值(getter/setter字段或var字段都是这样)。为什么我看不到价值观的改变?

    0 回复  |  直到 6 年前
        1
  •  1
  •   Greg    6 年前

    找到它…以防帮助他人。

    我在过滤错误的东西。scala中的setter的名称格式类似于“foo_u$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
      }