如何使用 Scala 反射获取方法的 return 类型字符串?

How to get return type string of a method using Scala reflection?

考虑以下代码:

object Foo{def foo(a:Int):List[(String, Int)] = ???}

class Bar{def bar(a:Int, b:Any):Option[(String, Long)] = ???}

给定对象或class,我需要先找到方法名称(似乎没有那么难)。

之后,对于每个方法,我想找到 Scala return 类型(不是 Java 类型)的字符串描述。因此,例如,对于 Foo.foo,我需要字符串 List[(String, Int)],对于 Bar.bar,我需要字符串 Option[(String, Long)].

我看过 this and this 教程,但无法理解。

编辑:这是我根据评论所做的尝试:

class RetTypeFinder(obj:AnyRef) {
  import scala.reflect.runtime.{universe => ru}
  val m = ru.runtimeMirror(getClass.getClassLoader)
  val im = m.reflect(obj)
  def getRetType(methodName:String) = {
    ru.typeOf[obj.type].declaration(ru.TermName(methodName)).asMethod.returnType
  }
}
object A { def foo(a:Int):String = ??? } // define dummy object
class B { def bar(a:Int):String = ??? } // define dummy class
val a = new RetTypeFinder(A) 
a.getRetType("foo")  // exception here
val b = new RetTypeFinder(new B) 
b.getRetType("bar")  // exception here

我得到的错误是:

scala.ScalaReflectionException: <none> is not a method
at scala.reflect.api.Symbols$SymbolApi$class.asMethod(Symbols.scala:228)
at scala.reflect.internal.Symbols$SymbolContextApiImpl.asMethod(Symbols.scala:84)
at cs.reflect.Test.getRetCls(Test.scala:11)
...

然而,这有效(在 REPL 中试过):

import scala.reflect.runtime.{universe => ru}
val m = ru.runtimeMirror(getClass.getClassLoader)

object A { def foo(a:Int):String = ??? } // define dummy object

val im = m.reflect(A)
ru.typeOf[A.type].declaration(ru.TermName("foo")).asMethod.returnType

class B { def bar(a:Int):String = ??? } // define dummy class

val im = m.reflect(new B)
ru.typeOf[B].declaration(ru.TermName("bar")).asMethod.returnType

我需要用第一种方式,我事先不知道objects/classes会传递什么。任何帮助将不胜感激。

一旦你有了一个 universe.Type 你就可以使用评论中的方法来获取它的方法之一的 return 类型:

import scala.reflect.runtime.{universe => ru}

def getRetTypeOfMethod(tpe: ru.Type)(methodName: String) =
  tpe.member(ru.TermName(methodName)).asMethod.returnType

要获得 universe.Type 最简单的方法是在隐式 TypeTag:

中捕获
class RetTypeFinder[T <: AnyRef](obj: T)(implicit tag: ru.TypeTag[T]) {
  def getRetType(methodName: String) = {
    val tpe = tag.tpe
    getRetTypeOfMethod(tpe)(methodName)
  }
}

但是如果你没有一个TypeTag,而只是一个AnyRef类型的对象,你可以通过一个mirror来反映它。由于 Java 的类型擦除,结果 Type 将丢失一些信息,但它仍然足以通过名称获取方法的 return 类型,因为 JVM 支持反思:

class RetTypeFinder2(obj: AnyRef) {
  def getRetType(methodName: String) = {
    val mirror = ru.runtimeMirror(getClass.getClassLoader)
    val tpe = mirror.reflect(obj).symbol.info
    getRetTypeOfMethod(tpe)(methodName)
  }
}

两种方法都能很好地解决您的问题:

scala> new RetTypeFinder(A).getRetType("foo")
res0: reflect.runtime.universe.Type = String

scala> new RetTypeFinder2(A).getRetType("foo")
res1: reflect.runtime.universe.Type = String

scala> new RetTypeFinder(new B).getRetType("bar")
res2: reflect.runtime.universe.Type = String

scala> new RetTypeFinder2(new B).getRetType("bar")
res3: reflect.runtime.universe.Type = String