简体   繁体   English

Scala 使用扩展 trait 的类的反射访问运行时类型的成员

[英]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 ,它将不起作用,因为MyItemnum作为方法,而不是字段(如在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).typeSignatureAnyRef 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM