Scala 获取对象的字段值并将其写入映射

Scala 获取对象的字段值并将其写入映射,scala,reflection,Scala,Reflection,我正在尝试将对象状态(带有特定字段)镜像到映射中 class ObjectState[T] { // object reference var objRef : T = _ // map that mirrors current object state var stateMap : Map[String, Any] = _ def registerFields(fields: List[String], objectInstance: T): Unit = { /

我正在尝试将对象状态(带有特定字段)镜像到映射中

class ObjectState[T] {
  // object reference
  var objRef : T = _
  // map that mirrors current object state
  var stateMap : Map[String, Any] = _

  def registerFields(fields: List[String], objectInstance: T): Unit = {
    // just register
    objRef = objectInstance
    stateMap = fields.map(field => field -> null)(collection.breakOut)
    Mirror.inMirrorObjectState(this)
  }

  object Mirror {
    // basically populate object state
    def inMirrorObjectState[T](state: ObjectState[T]): Unit = {
      val objectInstance = state.objRef
      stateMap.keySet foreach { key =>
        println(s"Attempt to find ${key}")
        println(s"Fields: ${objectInstance.getClass.getFields.size}") // zero field size
        val field = objectInstance.getClass.getField(key) // exception
        stateMap += (key -> field.get(objectInstance))
      }
    }
  }
}
我的对象实例如下所示

val obj = new BasicObject
obj.fieldA = "field"
obj.fieldB = 10

// 1. register object in OSM and get id - might be useful
val id = OSMPlatform.registerObject(obj, List("fieldA", "fieldB"))
  class BasicObject {
    var fieldA : String = _
    var fieldB : Int = _

    override def toString: String = {
      "Basic object: $fieldA : $fieldB"
    }
  }
BasicObject
如下所示

val obj = new BasicObject
obj.fieldA = "field"
obj.fieldB = 10

// 1. register object in OSM and get id - might be useful
val id = OSMPlatform.registerObject(obj, List("fieldA", "fieldB"))
  class BasicObject {
    var fieldA : String = _
    var fieldB : Int = _

    override def toString: String = {
      "Basic object: $fieldA : $fieldB"
    }
  }
然后我尝试创建我的
ObjectState
类的实例

  def registerObject(objectInstance: AnyRef, fields: List[String]): String = {
    val id = UUID.randomUUID().toString
    val objState = new ObjectState[AnyRef]
    objState.registerFields(fields, objectInstance)

    OSMContext.registerObject(id, objState)
    id
  }
但我得到了一个例外

Exception in thread "main" java.lang.NoSuchFieldException: fieldA
在那条线上

 val field = objectInstance.getClass.getField(key)

似乎找不到那块地。为什么呢?这是由于
T
类型造成的吗?

基于我的跑步记录,您应该使用:
getDeclaredField
并使用
setAccessible(true)
来避开非法访问异常

class ObjectState[T] {
  // object reference
  var objRef : T = _
  // map that mirrors current object state
  var stateMap : Map[String, Any] = _

  def registerFields(fields: List[String], objectInstance: T): Unit = {
    // just register
    objRef = objectInstance
    stateMap = fields.map(field => field -> null)(collection.breakOut)
    Mirror.inMirrorObjectState(this)
  }

  object Mirror {
    // basically populate object state
    def inMirrorObjectState[T](state: ObjectState[T]): Unit = {
      val objectInstance = state.objRef
      stateMap.keySet foreach { key =>
        println(s"Attempt to find ${key}")
        // zero field size
        val value = objectInstance.getClass.getDeclaredField(key) // exception
        value.setAccessible(true) // remote the IllegalAccessException
        stateMap += (key -> value.get(objectInstance))
      }
    }
  }
}

class Name () {
  var name: String = _
}

val obj = new Name();
obj.name = "H"

val objState = new ObjectState[AnyRef];
objState.registerFields(List("name"), obj);
objState.stateMap
希望这会有所帮助,但我认为对我来说使用setAccessible api就像是一个黑客

似乎这样更好,删除setAccessible并使用getDeclaredMethod(名称的获取者)然后调用它

scala> class ObjectState[T] {
     |   // object reference
     |   var objRef : T = _
     |   // map that mirrors current object state
     |   var stateMap : Map[String, Any] = _
     |
     |   def registerFields(fields: List[String], objectInstance: T): Unit = {
     |     // just register
     |     objRef = objectInstance
     |     stateMap = fields.map(field => field -> null)(collection.breakOut)
     |     Mirror.inMirrorObjectState(this)
     |   }
     |
     |   object Mirror {
     |     // basically populate object state
     |     def inMirrorObjectState[T](state: ObjectState[T]): Unit = {
     |       val objectInstance = state.objRef
     |       stateMap.keySet foreach { key =>
     |         println(s"Attempt to find ${key}")
     |         // zero field size
     |         val value = objectInstance.getClass.getDeclaredMethod(key) // exception
     |         stateMap += (key -> value.invoke(objectInstance))
     |       }
     |     }
     |   }
     | }
defined class ObjectState

scala>

scala> class Name () {
     |   var name: String = _
     | }
defined class Name

scala>

scala> val obj = new Name();
obj: Name = Name@fb21b91

scala> obj.name = "H"
obj.name: String = H

scala>

scala> val objState = new ObjectState[AnyRef];
objState: ObjectState[AnyRef] = ObjectState@688bf369

scala> objState.registerFields(List("name"), obj);
Attempt to find name

scala> objState.stateMap
res1: Map[String,Any] = Map(name -> H)

根据我的跑步记录,您应该使用:
getDeclaredField
,也可以使用
setAccessible(true)
来避开IllegaAccessException

class ObjectState[T] {
  // object reference
  var objRef : T = _
  // map that mirrors current object state
  var stateMap : Map[String, Any] = _

  def registerFields(fields: List[String], objectInstance: T): Unit = {
    // just register
    objRef = objectInstance
    stateMap = fields.map(field => field -> null)(collection.breakOut)
    Mirror.inMirrorObjectState(this)
  }

  object Mirror {
    // basically populate object state
    def inMirrorObjectState[T](state: ObjectState[T]): Unit = {
      val objectInstance = state.objRef
      stateMap.keySet foreach { key =>
        println(s"Attempt to find ${key}")
        // zero field size
        val value = objectInstance.getClass.getDeclaredField(key) // exception
        value.setAccessible(true) // remote the IllegalAccessException
        stateMap += (key -> value.get(objectInstance))
      }
    }
  }
}

class Name () {
  var name: String = _
}

val obj = new Name();
obj.name = "H"

val objState = new ObjectState[AnyRef];
objState.registerFields(List("name"), obj);
objState.stateMap
希望这会有所帮助,但我认为对我来说使用setAccessible api就像是一个黑客

似乎这样更好,删除setAccessible并使用getDeclaredMethod(名称的获取者)然后调用它

scala> class ObjectState[T] {
     |   // object reference
     |   var objRef : T = _
     |   // map that mirrors current object state
     |   var stateMap : Map[String, Any] = _
     |
     |   def registerFields(fields: List[String], objectInstance: T): Unit = {
     |     // just register
     |     objRef = objectInstance
     |     stateMap = fields.map(field => field -> null)(collection.breakOut)
     |     Mirror.inMirrorObjectState(this)
     |   }
     |
     |   object Mirror {
     |     // basically populate object state
     |     def inMirrorObjectState[T](state: ObjectState[T]): Unit = {
     |       val objectInstance = state.objRef
     |       stateMap.keySet foreach { key =>
     |         println(s"Attempt to find ${key}")
     |         // zero field size
     |         val value = objectInstance.getClass.getDeclaredMethod(key) // exception
     |         stateMap += (key -> value.invoke(objectInstance))
     |       }
     |     }
     |   }
     | }
defined class ObjectState

scala>

scala> class Name () {
     |   var name: String = _
     | }
defined class Name

scala>

scala> val obj = new Name();
obj: Name = Name@fb21b91

scala> obj.name = "H"
obj.name: String = H

scala>

scala> val objState = new ObjectState[AnyRef];
objState: ObjectState[AnyRef] = ObjectState@688bf369

scala> objState.registerFields(List("name"), obj);
Attempt to find name

scala> objState.stateMap
res1: Map[String,Any] = Map(name -> H)

是否也可以添加BasicObject定义,与BasicObject实例和registerFields调用没有当前关系。@LostMoican完成是否也可以添加BasicObject定义,与BasicObject实例和registerFields调用没有当前关系。@LostMoican完成