通过Scala中的反射调用方法

时间:2018-11-22 12:29:40

标签: scala reflection scala-reflect

我想通过反射调用任意东西的任意公共方法。即假设我想编写方法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: _*) } 版本的要求(例如,这些ClassTagTypeTag)(不退回纯Java反射)对我来说没关系吗?

1 个答案:

答案 0 :(得分:1)

是的,有。

  1. 首先,根据this answerextractMethodTypeTag[Stuff]严格更严格。尽管我们不会自动从隐式ClassTag[Stuff]中获取隐式ClassTag[Stuff],但我们可以将其手动评估为TypeTag[Stuff],然后将其隐式或显式传递给需要它的ClassTag[Stuff](stuffTypeTag.mirror.runtimeClass(stuffTypeTag.tpe)): / p>

    reflect
  2. 第二个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传递标签)进行比较。