[英]Scala accessing members of runtime types using reflection of a class that extends a trait
假設我有一個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
如果您打印出上面的theType
,它將是MyItem
。
此時,如果您嘗試使用 反射來修改字段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
不幸的是,上面會拋出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 類並修改其 field 。 如何獲取MyItem
類型的實例並修改MyItem
的方法num
正在訪問的MyItem.SubItem
中的字段?
代替
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).typeSignature
是AnyRef 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
)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.