如何使用 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)]
.
编辑:这是我根据评论所做的尝试:
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
考虑以下代码:
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)]
.
编辑:这是我根据评论所做的尝试:
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