我想通过反射调用任意东西的任意公共方法。即假设我想编写方法extractMethod
来使用:
class User { def setAvatar(avatar: Avatar): Unit = …; … }
val m = extractMethod(someUser, "setAvatar")
m(someAvatar)
从Scala文档的Reflection. Overview文档中,我看到了以下直接方法:
import scala.reflect.ClassTag
import scala.reflect.runtime.universe._
def extractMethod[Stuff: ClassTag: TypeTag](
stuff: Stuff,
methodName: String): MethodMirror =
{
val stuffTypeTag = typeTag[Stuff]
val mirror = stuffTypeTag.mirror
val stuffType = stuffTypeTag.tpe
val methodSymbol = stuffType
.member(TermName(methodName)).asMethod
mirror.reflect(stuff)
.reflectMethod(methodSymbol)
}
但是,我对这种解决方案感到困扰的是,我需要传递隐式的ClassTag[Stuff]
和TypeTag[Stuff]
参数(调用reflect
时需要第一个参数,第二个参数是获取{ {1}})。这可能非常麻烦,特别是如果从泛型调用stuffType
且从泛型调用等等。对于某些缺少运行时类型信息的语言,我认为这是必要的,但是Scala基于JRE,它允许执行以下操作:
extractMethod
我了解Scala反射可以获取比基本Java反射更多的信息。尽管如此,这里我只需要调用一个方法。假设性能可以通过某种方式降低基于Scala反射的def extractMethod[Stuff](
stuff: Stuff,
methodName: String,
parameterTypes: Array[Class[_]]): (Object*) => Object =
{
val unboundMethod = stuff.getClass()
.getMethod(methodName, parameterTypes: _*)
arguments => unboundMethod(stuff, arguments: _*)
}
版本的要求(例如,这些ClassTag
,TypeTag
)(不退回纯Java反射)对我来说没关系吗?
答案 0 :(得分:1)
是的,有。
首先,根据this answer,extractMethod
比TypeTag[Stuff]
严格更严格。尽管我们不会自动从隐式ClassTag[Stuff]
中获取隐式ClassTag[Stuff]
,但我们可以将其手动评估为TypeTag[Stuff]
,然后将其隐式或显式传递给需要它的ClassTag[Stuff](stuffTypeTag.mirror.runtimeClass(stuffTypeTag.tpe))
: / p>
reflect
第二个import scala.reflect.ClassTag
import scala.reflect.runtime.universe._
def extractMethod[Stuff: TypeTag](
stuff: Stuff,
methodName: String): MethodMirror =
{
val stuffTypeTag = typeTag[Stuff]
val mirror = stuffTypeTag.mirror
val stuffType = stuffTypeTag.tpe
val stuffClassTag = ClassTag[Stuff](mirror.runtimeClass(stuffType))
val methodSymbol = stuffType
.member(TermName(methodName)).asMethod
mirror.reflect(stuff)(stuffClassTag)
.reflectMethod(methodSymbol)
}
和mirror
可从stuffType
获得:
stuff.getClass()
因此,我们获得了Scala风格的反射实体(即最终为import scala.reflect.ClassTag
import scala.reflect.runtime.universe._
def extractMethod(stuff: Stuff, methodName: String): MethodMirror = {
val stuffClass = stuff.getClass()
val mirror = runtimeMirror(stuffClass.getClassLoader)
val stuffType = mirror.classSymbol(stuffClass).toType
val stuffClassTag = ClassTag[Stuff](mirror.runtimeClass(stuffType))
val methodSymbol = stuffType
.member(TermName(methodName)).asMethod
mirror.reflect(stuff)(stuffClassTag)
.reflectMethod(methodSymbol)
}
),而无需从调用者显式或隐式传递MethodMirror
和/或ClassTag
。但是,不确定如何在性能方面与问题中描述的方式(即从外部和纯Java传递标签)进行比较。