9

假设我有一个通用超类:

class GenericExample[T](
                         a: String,
                         b: T
                       ) {

  def fn(i: T): T = b
}

和一个具体的子类:

case class Example(
                    a: String,
                    b: Int
                  ) extends GenericExample[Int](a, b)

我想通过scala反射获取函数“fn”的类型参数,所以我选择并过滤它的成员:

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
        }
    }

我期待 scala 给我正确的结果,也就是说List(List(Int)),我只得到了通用的List(List(T))

仔细研究文档,我发现 typeSignature 是罪魁祸首:

 *  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.

它建议我使用替代方案:

def typeSignatureIn(site: Type): Type

但是,由于类 Example 不再是通用的,我无法从 typeTag[Example] 获取站点,谁能建议我如何仅给定 typeTag[Example] 获取 typeOf[Int]?或者没有办法做到这一点,我必须恢复到 Java 反射?

非常感谢你的帮助。

更新:经过一些快速测试后,我发现即使MethodSymbol.returnType也无法按预期工作,以下代码:

member.returnType

也 yield T,它不能被asSeenFrom纠正,因为下面的代码不会改变结果:

member.returnType.asSeenFrom(baseType.tpe, baseType.tpe.typeSymbol.asClass)
4

2 回答 2

13

我可以建议两种方法:

1) 从基类中揭示泛型类型:

import scala.reflect.runtime.universe._

class GenericExample[T: TypeTag](a: String, b: T) {
  def fn(i: T) = "" + b + i
}

case class Example(a: String, b: Int) extends GenericExample[Int](a, b) {}

val classType = typeOf[Example].typeSymbol.asClass
val baseClassType = typeOf[GenericExample[_]].typeSymbol.asClass
val baseType = internal.thisType(classType).baseType(baseClassType)

baseType.typeArgs.head // returns reflect.runtime.universe.Type = scala.Int

2)添加返回类型的隐式方法:

import scala.reflect.runtime.universe._

class GenericExample[T](a: String, b: T) {
  def fn(i: T) = "" + b + i
}

case class Example(a: String, b: Int) extends GenericExample[Int](a, b)

implicit class TypeDetector[T: TypeTag](related: GenericExample[T]) {
  def getType(): Type = {
    typeOf[T]
  }
}

new Example("", 1).getType() // returns reflect.runtime.universe.Type = Int
于 2016-07-06T10:37:18.090 回答
1

我正在发布我的解决方案:由于 Scala 的设计,我认为别无选择:

Scala 反射和 Java 反射中方法的核心区别在于柯里化:Scala 方法由许多对括号组成,首先调用带参数的方法只是构造一个可以接受更多括号对的匿名类,或者如果没有更多括号, 构造一个 NullaryMethod 类(也称为按名称调用),可以解析该类以产生方法的结果。因此,当方法已经分解为 Method & NullaryMethod Signatures 时,scala 方法的类型仅在此级别解决。

结果很明显,结果类型只能使用递归获得:

  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
  }

impl拥有该方法的类在哪里,symbol是您Type.member(s)通过 scala 反射获得的

于 2016-07-19T18:54:10.807 回答