【问题标题】:Calling method via reflection in Scala在Scala中通过反射调用方法
【发布时间】:2018-11-22 12:29:39
【问题描述】:

我想通过反射调用任意东西的任意公共方法。 IE。比方说,我想编写方法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,第二个用于获取 stuffType)。这可能非常麻烦,特别是如果从泛型调用的泛型调用extractMethod 等等。对于某些严重缺乏运行时类型信息的语言,我认为这是必要的,但 Scala 基于 JRE,它允许执行以下操作:

def extractMethod[Stuff](
  stuff:          Stuff,
  methodName:     String,
  parameterTypes: Array[Class[_]]): (Object*) => Object =
    {
      val unboundMethod = stuff.getClass()
        .getMethod(methodName, parameterTypes: _*)
      arguments => unboundMethod(stuff, arguments: _*)
    }

我了解 Scala 反射允许获取比基本 Java 反射更多的信息。不过,在这里我只需要调用一个方法。有没有办法以某种方式减少基于 Scala 反射的extractMethod 版本的要求(例如这些ClassTagTypeTag)(不回退到纯 Java 反射),假设性能对我来说并不重要?

【问题讨论】:

    标签: scala reflection scala-reflect


    【解决方案1】:

    是的,有。

    1. 首先,根据this answerTypeTag[Stuff] 是比ClassTag[Stuff] 严格更强的要求。虽然我们不会从隐式TypeTag[Stuff] 自动获取隐式ClassTag[Stuff],但我们可以手动将其评估为ClassTag[Stuff](stuffTypeTag.mirror.runtimeClass(stuffTypeTag.tpe)),然后将其隐式或显式传递给需要它的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)
        }
      
    2. 第二,mirrorstuffType可以从stuff.getClass()获取:

      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)
      }
      

    因此,我们获得了 Scala 风格的反射实体(即最终 MethodMirror),而不需要从调用者显式或隐式传递 ClassTag 和/或 TypeTag。但是,不确定它在性能方面与问题中描述的方式(即从外部和纯 Java 传递标签)相比如何。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-21
      • 2015-03-19
      • 2016-04-25
      相关资源
      最近更新 更多