[英]Scala accessing members of runtime types using reflection of a class that extends a trait
Suppose I have a MyItem
trait and its companion object has an apply()
function that creates a class instance called SubItem
that extends from MyItem
:假设我有一个
MyItem
trait 并且它的伴生对象有一个apply()
函数,它创建一个名为SubItem
的类实例,它从MyItem
扩展:
import scala.reflect.runtime.{universe => ru}
trait MyItem {
import MyItem._
def num: Option[Int]
}
object MyItem {
class SubItem(val num: Option[Int]) extends MyItem
def apply(num: Option[Int]): MyItem = new SubItem(num) // creates SubItem
}
def getTypeTag[T: ru.TypeTag](obj: T) = ru.typeTag[T]
val modifiedItem = MyItem(Some(11))
val theType = getTypeTag(modifiedItem).tpe
If you print out theType
above, it will be MyItem
.如果您打印出上面的
theType
,它将是MyItem
。
At this point if you try to use reflection to modify the field num
, it's not going to work because MyItem
has num
as a method , not a field (as in MyItem.SubItem
):此时,如果您尝试使用 反射来修改字段
num
,它将不起作用,因为MyItem
将num
作为方法,而不是字段(如在MyItem.SubItem
):
val m = ru.runtimeMirror(modifiedItem.getClass.getClassLoader)
val numTermSymb = theType.decl(ru.TermName("num")).asTerm
val im = m.reflect(modifiedItem)
val numFieldMirror = im.reflectField(numTermSymb) // not going to work
numFieldMirror.get
numFieldMirror.set(Some(999)) // my goal, if possible
Unfortunately, above will throw out scala.ScalaReflectionException: expected a field or an accessor method symbol, you provided method num
.不幸的是,上面会抛出
scala.ScalaReflectionException: expected a field or an accessor method symbol, you provided method num
。
Instead you should do the following:相反,您应该执行以下操作:
val numTermSymb = theType.decl(ru.TermName("num")).asMethod
val im = m.reflect(modifiedItem)
val numFieldMirror = im.reflectMethod(numTermSymb)
numFieldMirror() // returns `Some(11)`
But my goal is to access the SubItem class that extends MyItem and modify its field .但我的目标是访问扩展 MyItem 的 SubItem 类并修改其 field 。 How can I get an instance of the type
MyItem
and modify the field in MyItem.SubItem
that MyItem
's method num
is accessing?如何获取
MyItem
类型的实例并修改MyItem
的方法num
正在访问的MyItem.SubItem
中的字段?
Replace代替
val theType = getTypeTag(modifiedItem).tpe
with和
val theType = ru.typeOf[MyItem.SubItem]
if you know the name of class statically or with如果你知道类的名称静态或
val theType = m.staticClass("pckg.MyItem.SubItem").typeSignature
if you know the name of class dynamically.如果您动态知道类的名称。
Try尝试
val className = modifiedItem.getClass.getName.replace('$', '.')
val theType = m.staticClass(className).typeSignature
Actually, m.staticClass(className).typeSignature
is AnyRef with pckg.MyItem {...}
ie parents/decls of SubItem
实际上,
m.staticClass(className).typeSignature
是AnyRef with pckg.MyItem {...}
即子项的父母/ SubItem
theType =:= ru.typeOf[MyItem.SubItem] // false
so, although numFieldMirror.get/set
work, it's better to use toType
instead of typeSignature
所以,虽然
numFieldMirror.get/set
有效,但最好使用toType
而不是typeSignature
val className = modifiedItem.getClass.getName.replace('$', '.')
val theType = m.staticClass(className).toType
theType =:= ru.typeOf[MyItem.SubItem] // true
One more way is purely Scala-like另一种方式是纯 Scala 式的
val instanceMirror = m.reflect(modifiedItem)
val theType = instanceMirror.symbol.toType
theType =:= ru.typeOf[MyItem.SubItem] // true
It's even better because doesn't use error-prone and implementation-dependent operations on strings ( replace
).它甚至更好,因为不对字符串使用容易出错和依赖于实现的操作(
replace
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.