Reputation: 25836
I want to get real Type
of generic value class parameter. I tried:
import scala.reflect.runtime.universe._
case class Test[T](value: T) extends AnyVal
object Main extends App {
val tag = implicitly[TypeTag[Test[String]]]
val constructor = tag.tpe.members.collect {
case m: MethodSymbol if m.isPrimaryConstructor => m
}.headOption
val constructorParams = constructor.map(_.paramLists.flatten).collect {
case param :: Nil => param
}
constructorParams.map(_.typeSignature).foreach(println)
}
but it prints:
T
I know that I can get type arguments with:
tag.tpe.typeArgs.foreach(println)
which outputs:
String
but Test
class can be defined like that:
case class Test[T](value: List[T]) extends AnyVal
so type of type argument and parameter type are different.
How can I do this?
Upvotes: 2
Views: 513
Reputation: 1894
There's an operation in the compiler called "as seen from" which returns the type of a member with respect to a specific owner type.
Here's an example in a REPL session:
Welcome to Scala 2.12.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_102).
Type in expressions for evaluation. Or try :help.
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> case class Test[T](value: List[T]) extends AnyVal
defined class Test
scala> val tp = typeOf[Test[String]]
tp: reflect.runtime.universe.Type = Test[String]
scala> val cls = tp.typeSymbol
cls: reflect.runtime.universe.Symbol = class Test
scala> val constructor = tp.members.collect({case m: MethodSymbol if m.isPrimaryConstructor => m}).head
constructor: reflect.runtime.universe.MethodSymbol = constructor Test
scala> constructor.info.asSeenFrom(tp, cls).paramLists.head.head.info
res0: reflect.runtime.universe.Type = scala.List[String]
scala> // alternatively
scala> constructor.infoIn(tp).paramLists.head.head.info
res1: reflect.runtime.universe.Type = scala.List[String]
Upvotes: 1