在Scala反射中,如何获取具体子类的泛型类型参数? [英] In Scala Reflection, How to get generic type parameter of a concrete subclass?

查看:423
本文介绍了在Scala反射中,如何获取具体子类的泛型类型参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个Generic超类:

pre $ class GenericExample [T](
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反射中,如何获取具体子类的泛型类型参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆