In Scala Reflection, How do I get the general type type of a particular subclass?

Assuming I have a general superclass:

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

and specific subclass:

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

I want to get a parameter of the type of the function "fn" when scala is reflected, so I select and filter through its elements:

 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 expected scala to give me the correct result, which is List(List(Int)) , instead I only got a generic List(List(T))

Having folded the document, I found that the Signature type 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 he offers me to use an alternative:

 def typeSignatureIn(site: Type): Type 

However, since the Example class is no longer generic, I cannot get the site from typeTag [Example], can anyone suggest me how to get typeOf [Int] only for typeTag [Example]? Or is there no way to do this, and should I return to Java reflection?

Many thanks for your help.

UPDATE: After some quick test, I found that even MethodSymbol.returnType is not working properly, the following code:

 member.returnType 

also gives T , annd cannot be fixed asSeenFrom, since the following code does not change the result:

 member.returnType.asSeenFrom(baseType.tpe, baseType.tpe.typeSymbol.asClass) 
+5
source share
2 answers

I am posting my solution: I think there is no alternative due to Scala design:

The main difference between methods in Scala reflection and Java reflection is currying: the Scala method consists of many pairs of brackets, calling the method with arguments first simply creates an anonymous class that can take more pairs of brackets, or if there is no more bracket on the left, the NullaryMethod class is created (aka call-by-name), which can be resolved to get the result of the method. Thus, the Scala method types are only allowed at this level, when the method is already broken down into Method and NullaryMethod signatures.

As a result, it becomes clear that the type of result can only be obtained 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 to which the method belongs, and symbol is what you got from Type.member(s) on Scala reflection

0
source

There are two approaches that I can offer:

1) Derive a generic type from the 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 an implicit method that returns a 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 
+4
source

All Articles