Reflection API - how to get actual types of a constructor of a generic object?

405 views
Skip to first unread message

Piotr Kolaczkowski

unread,
May 25, 2014, 7:23:52 AM5/25/14
to scala...@googlegroups.com
scala> class Generic[T](arg: T)
defined class Generic

scala> typeOf[Generic[Int]]
res37: reflect.runtime.universe.Type = Generic[Int]   // fine, so a full type info is here

scala> typeOf[Generic[Int]].declaration(nme.CONSTRUCTOR).asMethod.paramss(0).map(_.asTerm.typeSignature)
res38: List[reflect.runtime.universe.Type] = List(T)

T? Huh. Looks like we get a formal type here, not the actual. So how to get the actual type Int here? 

I know I can grab the actual type args for the whole class type by using match { case TypeRef(_, _, args) => args }.
Then possibly use the "substituteTypes" method, but I  didn't figure it out how to make it all fit together.

Can you give me a hint how to get actual unerased types of arguments to any method/constructor obtained by reflection?

Thanks,
Piotr





Oliver Ruebenacker

unread,
May 25, 2014, 7:35:43 AM5/25/14
to Piotr Kolaczkowski, scala-user

     Hello,

On Sun, May 25, 2014 at 7:23 AM, Piotr Kolaczkowski <pkol...@datastax.com> wrote:
scala> class Generic[T](arg: T)
defined class Generic

scala> typeOf[Generic[Int]]
res37: reflect.runtime.universe.Type = Generic[Int]   // fine, so a full type info is here

  Full type info is there at compile time, not at run-time.

     Best,
     Oliver
 

scala> typeOf[Generic[Int]].declaration(nme.CONSTRUCTOR).asMethod.paramss(0).map(_.asTerm.typeSignature)
res38: List[reflect.runtime.universe.Type] = List(T)

T? Huh. Looks like we get a formal type here, not the actual. So how to get the actual type Int here? 

I know I can grab the actual type args for the whole class type by using match { case TypeRef(_, _, args) => args }.
Then possibly use the "substituteTypes" method, but I  didn't figure it out how to make it all fit together.

Can you give me a hint how to get actual unerased types of arguments to any method/constructor obtained by reflection?

Thanks,
Piotr





--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Oliver Ruebenacker
Be always grateful, but never satisfied.

Piotr Kolaczkowski

unread,
May 25, 2014, 7:51:45 AM5/25/14
to scala...@googlegroups.com
Wrong, for sure it is there at runtime. 
Actually I managet to do it, but it is limited to a constructors or methods without generic arguments, and it is slightly convoluted solution:

  private val tpe = implicitly[TypeTag[T]].tpe
  private val paramTypes: Array[Type] = {
    val formalTypeArgs = tpe.typeSymbol.asClass.typeParams
    val actualTypeArgs = tpe match {
      case TypeRef(_, _, args) => args
      case _ => List.empty[Type]
    }
    val scalaConstructor: MethodSymbol = tpe.declaration(nme.CONSTRUCTOR).asMethod
    def symbolToType(s: Symbol) = s.asTerm.typeSignature.substituteTypes(formalTypeArgs, actualTypeArgs)
    scalaConstructor.paramss(0).map(symbolToType).toArray
  }

I'd appreciate if someone has a better, more-elegant solution.

Thanks,
Piotr

Eugene Burmako

unread,
May 25, 2014, 7:58:14 AM5/25/14
to Piotr Kolaczkowski, scala...@googlegroups.com
Use typeSignatureIn instead of typeSignature - that should help.

Oliver Ruebenacker

unread,
May 25, 2014, 8:00:02 AM5/25/14
to Piotr Kolaczkowski, scala-user

     Hello,

  Yes, you can capture type information at compile time and store it in an extra variable.

  I thought you were attempting to query an actual object for the types of its declared parameters.

     Best,
     Oliver


--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jason Zaugg

unread,
May 25, 2014, 8:08:36 AM5/25/14
to Eugene Burmako, Piotr Kolaczkowski, scala...@googlegroups.com
On Sun, May 25, 2014 at 1:57 PM, Eugene Burmako <xen...@gmail.com> wrote:
Use typeSignatureIn instead of typeSignature - that should help.

Yep, that's the one. 
scala> val tp = typeOf[Generic[Int]]
tp: reflect.runtime.universe.Type = Generic[Int]

scala> val constructor = tp.declaration(termNames.CONSTRUCTOR).asMethod
constructor: reflect.runtime.universe.MethodSymbol = constructor Generic

scala> val typeSig = constructor.typeSignatureIn(tp)
typeSig: reflect.runtime.universe.Type = (arg: Int)Generic[Int]

Type#declaration just gives you back a Symbol representing a method. You would get the same symbol back from typeOf[Generic[_].decl(...)

-jason

Piotr Kolaczkowski

unread,
May 25, 2014, 8:18:34 AM5/25/14
to scala...@googlegroups.com, Eugene Burmako, Piotr Kolaczkowski
Nice! That works! Thanks.
Reply all
Reply to author
Forward
0 new messages