scala - 如何使用 Scala 反射获取方法的返回类型字符串?

标签 scala scala-reflect

考虑以下代码:

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

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

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

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

我看到了thisthis教程,但无法弄清楚。

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

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

我需要以第一种方式使用它,我事先不知道将传递哪些对象/类。任何帮助将不胜感激。

最佳答案

一旦有了 universe.Type,您就可以使用注释中的方法来获取其方法之一的返回类型:

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类型的对象,你可以通过一个镜像来反射(reflect)它。由于 Java 的类型删除,生成的 Type 将丢失一些信息,但它仍然足以通过名称获取方法的返回类型,因为 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

关于scala - 如何使用 Scala 反射获取方法的返回类型字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38947338/

相关文章:

scala - 单元测试异步 Scala 代码

scala - 如何使用 SBT 使用 build.scala 中的 -D 变量?

scala - 从Scala读取Parquet文件而不使用Spark

java - 如何从 Scala 中的泛型类型获取字段名称和字段类型?

java - 实例化参数类型的空对象

scala - 如何创建一个以spark.broadcast[Map]作为参数的方法?

scala - map.get 为空键返回 None 但不为空值返回

scala - 从 TypeTag 和方法中获取精确的返回类型

Scala 反射 : Invoking a Function1's apply method - multiple alternatives?

scala - 没有适用于案例类别类型的 TypeTag