In Scala Reflection, How to get generic type parameter of a concrete subclass?

后端 未结 2 820
攒了一身酷
攒了一身酷 2020-12-19 03:53

Assuming that I have a Generic superclass:

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


        
相关标签:
2条回答
  • 2020-12-19 04:22

    There are two approaches which I can suggest:

    1) Reveal generic type from base class:

    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) Add implicit method which returns type:

    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
    
    0 讨论(0)
  • 2020-12-19 04:24

    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

    0 讨论(0)
提交回复
热议问题