从 Scala 中的 MethodMirror 实例获取方法的函数类型
Getting method's function type from the MethodMirror instance in Scala
假设我为某个对象的某个方法创建了一个 MethodMirror
实例。通过镜像的字段,我可以轻松访问 return 类型和方法的参数。但我实际上需要获取此方法作为函数的类型。
这是一个玩具代码示例,可以帮助我解释我想要实现的目标。我正在使用 Scala 2.11.6.
import scala.reflect.runtime.universe._
object ForWhosebug {
object Obj {
def method(x:String, y:String):Int = 0
def expectedRetType():((String, String) => Int) = ???
}
def main(args: Array[String]) {
val mirror:Mirror = runtimeMirror(getClass.getClassLoader)
val instanceMirror = mirror.reflect(Obj)
val methodSymbol:MethodSymbol = instanceMirror.symbol.toType.decl(TermName("method")).asMethod
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
println(methodMirror.symbol.returnType)
println(methodMirror.symbol.paramLists(0).map { x => x.info.resultType }.mkString(", "))
val expectedSymbol:MethodSymbol = instanceMirror.symbol.toType.decl(TermName("expectedRetType")).asMethod
println("I would like to produce from a 'methodMirror' this: "+expectedSymbol.returnType)
}
}
我想从 methodMirror
生成 Type
实例,它代表一个函数。对于此示例,它应该是 (String, String) => Int
。我更喜欢不太依赖于具体 Scala FunctionX
类.
的解决方案
下面的方法 getEtaExpandedMethodType
可以满足您的要求,甚至可以处理具有多个参数列表的方法。
另一方面,它不处理泛型方法。例如 def method[T](x: T) = 123
,当 eta 扩展时,创建一个类型为 Any => Int
的函数,但是 getEtaExpandedMethodType
将报告 T => Int
这不仅不正确而且根本没有意义( T
在此上下文中没有任何意义)。
def getEtaExpandedMethodType(methodSymbol: MethodSymbol): Type = {
val typ = methodSymbol.typeSignature
def paramType(paramSymbol: Symbol): Type = {
// TODO: handle the case where paramSymbol denotes a type parameter
paramSymbol.typeSignatureIn(typ)
}
def rec(paramLists: List[List[Symbol]]): Type = {
paramLists match {
case Nil => methodSymbol.returnType
case params :: otherParams =>
val functionClassSymbol = definitions.FunctionClass(params.length)
appliedType(functionClassSymbol, params.map(paramType) :+ rec(otherParams))
}
}
if (methodSymbol.paramLists.isEmpty) { // No arg method
appliedType(definitions.FunctionClass(0), List(methodSymbol.returnType))
} else {
rec(methodSymbol.paramLists)
}
}
def getEtaExpandedMethodType(methodMirror: MethodMirror): Type = getEtaExpandedMethodType(methodMirror.symbol)
REPL 测试:
scala> val mirror: Mirror = runtimeMirror(getClass.getClassLoader)
mirror: reflect.runtime.universe.Mirror = ...
scala> val instanceMirror = mirror.reflect(Obj)
instanceMirror: reflect.runtime.universe.InstanceMirror = instance mirror for Obj$@21b6e507
scala> val tpe = instanceMirror.symbol.toType
tpe: reflect.runtime.universe.Type = Obj.type
scala> getEtaExpandedMethodType(tpe.decl(TermName("method1")).asMethod)
res28: reflect.runtime.universe.Type = (String, String) => scala.Int
scala> getEtaExpandedMethodType(tpe.decl(TermName("method2")).asMethod)
res29: reflect.runtime.universe.Type = () => String
scala> getEtaExpandedMethodType(tpe.decl(TermName("method3")).asMethod)
res30: reflect.runtime.universe.Type = () => scala.Long
scala> getEtaExpandedMethodType(tpe.decl(TermName("method4")).asMethod)
res31: reflect.runtime.universe.Type = String => (scala.Float => scala.Double)
scala> getEtaExpandedMethodType(tpe.decl(TermName("method5")).asMethod)
res32: reflect.runtime.universe.Type = T => scala.Int
scala> getEtaExpandedMethodType(tpe.decl(TermName("method6")).asMethod)
res33: reflect.runtime.universe.Type = T => scala.Int
这可能是使用 universe.appliedType
的最直接的解决方案。它在多个参数列表的情况下不起作用。我 post 这是为了展示解决此问题的另一种方法。
def getEtaExpandedMethodType2(methodSymbol: MethodSymbol): Type = {
val typesList = methodSymbol.info.paramLists(0).map(x => x.typeSignature) :+ methodSymbol.returnType
val arity = methodSymbol.paramLists(0).size
universe.appliedType(definitions.FunctionClass(arity), typesList)
}
假设我为某个对象的某个方法创建了一个 MethodMirror
实例。通过镜像的字段,我可以轻松访问 return 类型和方法的参数。但我实际上需要获取此方法作为函数的类型。
这是一个玩具代码示例,可以帮助我解释我想要实现的目标。我正在使用 Scala 2.11.6.
import scala.reflect.runtime.universe._
object ForWhosebug {
object Obj {
def method(x:String, y:String):Int = 0
def expectedRetType():((String, String) => Int) = ???
}
def main(args: Array[String]) {
val mirror:Mirror = runtimeMirror(getClass.getClassLoader)
val instanceMirror = mirror.reflect(Obj)
val methodSymbol:MethodSymbol = instanceMirror.symbol.toType.decl(TermName("method")).asMethod
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
println(methodMirror.symbol.returnType)
println(methodMirror.symbol.paramLists(0).map { x => x.info.resultType }.mkString(", "))
val expectedSymbol:MethodSymbol = instanceMirror.symbol.toType.decl(TermName("expectedRetType")).asMethod
println("I would like to produce from a 'methodMirror' this: "+expectedSymbol.returnType)
}
}
我想从 methodMirror
生成 Type
实例,它代表一个函数。对于此示例,它应该是 (String, String) => Int
。我更喜欢不太依赖于具体 Scala FunctionX
类.
下面的方法 getEtaExpandedMethodType
可以满足您的要求,甚至可以处理具有多个参数列表的方法。
另一方面,它不处理泛型方法。例如 def method[T](x: T) = 123
,当 eta 扩展时,创建一个类型为 Any => Int
的函数,但是 getEtaExpandedMethodType
将报告 T => Int
这不仅不正确而且根本没有意义( T
在此上下文中没有任何意义)。
def getEtaExpandedMethodType(methodSymbol: MethodSymbol): Type = {
val typ = methodSymbol.typeSignature
def paramType(paramSymbol: Symbol): Type = {
// TODO: handle the case where paramSymbol denotes a type parameter
paramSymbol.typeSignatureIn(typ)
}
def rec(paramLists: List[List[Symbol]]): Type = {
paramLists match {
case Nil => methodSymbol.returnType
case params :: otherParams =>
val functionClassSymbol = definitions.FunctionClass(params.length)
appliedType(functionClassSymbol, params.map(paramType) :+ rec(otherParams))
}
}
if (methodSymbol.paramLists.isEmpty) { // No arg method
appliedType(definitions.FunctionClass(0), List(methodSymbol.returnType))
} else {
rec(methodSymbol.paramLists)
}
}
def getEtaExpandedMethodType(methodMirror: MethodMirror): Type = getEtaExpandedMethodType(methodMirror.symbol)
REPL 测试:
scala> val mirror: Mirror = runtimeMirror(getClass.getClassLoader)
mirror: reflect.runtime.universe.Mirror = ...
scala> val instanceMirror = mirror.reflect(Obj)
instanceMirror: reflect.runtime.universe.InstanceMirror = instance mirror for Obj$@21b6e507
scala> val tpe = instanceMirror.symbol.toType
tpe: reflect.runtime.universe.Type = Obj.type
scala> getEtaExpandedMethodType(tpe.decl(TermName("method1")).asMethod)
res28: reflect.runtime.universe.Type = (String, String) => scala.Int
scala> getEtaExpandedMethodType(tpe.decl(TermName("method2")).asMethod)
res29: reflect.runtime.universe.Type = () => String
scala> getEtaExpandedMethodType(tpe.decl(TermName("method3")).asMethod)
res30: reflect.runtime.universe.Type = () => scala.Long
scala> getEtaExpandedMethodType(tpe.decl(TermName("method4")).asMethod)
res31: reflect.runtime.universe.Type = String => (scala.Float => scala.Double)
scala> getEtaExpandedMethodType(tpe.decl(TermName("method5")).asMethod)
res32: reflect.runtime.universe.Type = T => scala.Int
scala> getEtaExpandedMethodType(tpe.decl(TermName("method6")).asMethod)
res33: reflect.runtime.universe.Type = T => scala.Int
这可能是使用 universe.appliedType
的最直接的解决方案。它在多个参数列表的情况下不起作用。我 post 这是为了展示解决此问题的另一种方法。
def getEtaExpandedMethodType2(methodSymbol: MethodSymbol): Type = {
val typesList = methodSymbol.info.paramLists(0).map(x => x.typeSignature) :+ methodSymbol.returnType
val arity = methodSymbol.paramLists(0).size
universe.appliedType(definitions.FunctionClass(arity), typesList)
}