3

考虑以下代码:

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

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

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

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

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

4

1 回答 1

3

一旦你有了 a 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)
  }
}

但是如果你没有 a TypeTag,而只是一个 type 的对象AnyRef,你可以通过 amirror来反映它。由于 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
于 2016-08-15T14:48:59.980 回答