微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决Scala使用扩展特征的类的反射来访问运行时类型的成员

假设我有一个MyItem特征,并且它的伴随对象有一个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

如果您在上方打印出theType,它将是MyItem

在这一点上,如果您尝试使用reflection修改字段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

不幸的是,上面将抛出scala.ScalaReflectionException: expected a field or an accessor method symbol,you provided method num

相反,您应该执行以下操作:

val numTermSymb = theType.decl(ru.TermName("num")).asMethod
val im = m.reflect(modifiedItem)
val numFieldMirror = im.reflectMethod(numTermSymb)
numFieldMirror() // returns `Some(11)`

但是我的目标是访问扩展MyItem的SubItem类并修改其字段。如何获取类型MyItem的实例并修改MyItem.SubItem方法MyItem正在访问的num中的字段?

解决方法

替换

val theType = getTypeTag(modifiedItem).tpe

使用

val theType = ru.typeOf[MyItem.SubItem]

如果您是静态知道类名或使用

val theType = m.staticClass("pckg.MyItem.SubItem").typeSignature

如果您动态知道类的名称。


尝试

val className = modifiedItem.getClass.getName.replace('$','.')
val theType = m.staticClass(className).typeSignature

实际上,m.staticClass(className).typeSignatureAnyRef with pckg.MyItem {...},即SubItem的父母/决定

theType =:= ru.typeOf[MyItem.SubItem] // false

因此,尽管numFieldMirror.get/set有效,但最好使用toType代替typeSignature

val className = modifiedItem.getClass.getName.replace('$','.')
val theType = m.staticClass(className).toType

theType =:= ru.typeOf[MyItem.SubItem] // true

另一种方式是完全像Scala一样

val instanceMirror = m.reflect(modifiedItem) 
val theType = instanceMirror.symbol.toType

theType =:= ru.typeOf[MyItem.SubItem] // true

更好,因为它不对字符串(replace)使用易于出错且依赖于实现的操作。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。