问题描述
假设我有一个Generic超类:
pre $ class GenericExample [T](
$ p $
a:String,
b:T
){
def fn(i:T):T = b
}
和一个具体的子类:
case类示例(
a :字符串,
b:Int
)扩展GenericExample [Int](a,b)
我想通过scala反射来获取函数fn的类型参数,所以我选择并过滤其成员:
import ScalaReflection.universe._
val baseType = typeTag [示例]
val成员= baseType
.tpe
.member(methodName: TermName)
.asTerm
.alternatives
.map(_。asMethod)
.head
val paramss = member.paramss
val actualTypess:List [List [Type]] = paramss.map {
params =>
params.map {
param =>
param.typeSignature
我期待scala给我正确的结果,它是 List(List(Int))
,而我只有通用的 List(List(T))
在文档中琢磨我发现typeSignature是罪魁祸首:
*该方法总是以最通用的方式返回签名,即使底层符号是从泛型类型的
*实例获得的。
这表明我使用了另一种方法:
def typeSignatureIn(site:Type):Type
但是,由于类示例不再是通用的,我没有办法从typeTag [示例]获取网站,任何人都可以告诉我如何获得typeOf [Int]只给typeTag [示例]?或者有没有办法做到这一点,我不得不恢复到Java反射?
非常感谢您的帮助。
更新:经过一些快速测试后,我发现即使是 MethodSymbol.returnType 也无法按预期工作,如下代码:
member.returnType
T
,并且不能通过 asSeenFrom 更正,因为以下代码不会更改结果:
member.returnType.asSeenFrom(baseType.tpe,baseType.tpe.typeSymbol.asClass)
我发布了我的解决方案:我认为Scala的设计没有别的选择:
Scala反射& Java反射是currying:Scala方法由多对括号组成,调用一个带参数的方法首先构造一个匿名类,它可以带多个括号对,或者如果没有更多的括号,则构造一个NullaryMethod类(也称为call-按名称),可以解决该问题以产生该方法的结果。因此,只有在方法已经分解为Method& NullaryMethod签名。
结果很明显,结果类型只能使用递归获得:
private def methodSignatureToParameter_ReturnTypes(tpe:Type):(List [List [Type]],Type)= {
tpe match {
case n:NullaryMethodType =>
无 - > n.resultType
case m:MethodType =>
val paramTypes:List [Type] = m.params.map(_。typeSignatureIn(tpe))
val downstream = methodSignatureToParameter_ReturnTypes(m.resultType)
downstream.copy(_1 = List( paramTypes)++ methodSignatureToParameter_ReturnTypes(m.resultType)._ 1)
case _ =>
无 - > tpe
}
}
def getParameter_ReturnTypes(symbol:MethodSymbol,impl:Type)= {
$ b $ val签名= symbol.typeSignatureIn(impl)
val result = methodSignatureToParameter_ReturnTypes(签名)
结果
}
哪里 impl
是拥有该方法的类,并且符号
是您从 Type中获得的。会员
scala反射
Assuming that I have a Generic superclass:
class GenericExample[T](
a: String,
b: T
) {
def fn(i: T): T = b
}
and a concrete subclass:
case class Example(
a: String,
b: Int
) extends GenericExample[Int](a, b)
I want to get the type parameter of function "fn" by scala reflection, so I select and filter through its members:
import ScalaReflection.universe._
val baseType = typeTag[Example]
val member = baseType
.tpe
.member(methodName: TermName)
.asTerm
.alternatives
.map(_.asMethod)
.head
val paramss = member.paramss
val actualTypess: List[List[Type]] = paramss.map {
params =>
params.map {
param =>
param.typeSignature
}
}
I was expecting scala to give me the correct result, which is List(List(Int))
, instead I only got the generic List(List(T))
Crunching through the document I found that typeSignature is the culprit:
* This method always returns signatures in the most generic way possible, even if the underlying symbol is obtained from an
* instantiation of a generic type.
And it suggests me to use the alternative:
def typeSignatureIn(site: Type): Type
However, since class Example is no longer generic, there is no way I can get site from typeTag[Example], can anyone suggest me how to get typeOf[Int] given only typeTag[Example]? Or there is no way to do it and I have to revert to Java reflection?
Thanks a lot for your help.
UPDATE: After some quick test I found that even MethodSymbol.returnType doesn't work as intended, the following code:
member.returnType
also yield T
, annd it can't be corrected by asSeenFrom, as the following code doesn't change the result:
member.returnType.asSeenFrom(baseType.tpe, baseType.tpe.typeSymbol.asClass)
I'm posting my solution: I think there is no alternative due to Scala's design:
The core difference between methods in Scala reflection & Java reflection is currying: Scala method comprises of many pairs of brackets, calling a method with arguments first merely constructs an anonymous class that can take more pairs of brackets, or if there is no more bracket left, constructs a NullaryMethod class (a.k.a. call-by-name) that can be resolved to yield the result of the method. So types of scala method is only resolved at this level, when method is already broken into Method & NullaryMethod Signatures.
As a result it becomes clear that the result type can only be get using recursion:
private def methodSignatureToParameter_ReturnTypes(tpe: Type): (List[List[Type]], Type) = {
tpe match {
case n: NullaryMethodType =>
Nil -> n.resultType
case m: MethodType =>
val paramTypes: List[Type] = m.params.map(_.typeSignatureIn(tpe))
val downstream = methodSignatureToParameter_ReturnTypes(m.resultType)
downstream.copy(_1 = List(paramTypes) ++ methodSignatureToParameter_ReturnTypes(m.resultType)._1)
case _ =>
Nil -> tpe
}
}
def getParameter_ReturnTypes(symbol: MethodSymbol, impl: Type) = {
val signature = symbol.typeSignatureIn(impl)
val result = methodSignatureToParameter_ReturnTypes(signature)
result
}
Where impl
is the class that owns the method, and symbol
is what you obtained from Type.member(s)
by scala reflection
这篇关于在Scala反射中,如何获取具体子类的泛型类型参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!