0

Given a string of com.company.util.Type[com.company.controller.Response], or com.company.util.Type[scala.collection.Seq[com.company.controller.Response]] how do you use reflection to instantiate com.company.controller.Response?

I could manually use regex or the sort to find the most nested [], but my next task is to try and retain as much information of how it's wrapped as possible, for instance: I would like to know if it's been wrapped in a Seq, or Option or Some.

Using

implicit val mirror = runTimeMirror(classLoader)
mirror.staticClass("com.company.controller.Response").toType

Gives me what I want, but as soon as you introduce nested class path's like above, it understandably throws a not found exception.

speak
  • 5,202
  • 4
  • 36
  • 41
  • 1
    Runtime reflection don't see type parameters. It's just `com.company.util.Type` in runtime. Runtime Di like Guice uses annotations and providers to work around it. In Scala it makes sense to use compile time reflection as then then types are actually known. – Mateusz Kubuszok Apr 02 '20 at 11:36
  • @MateuszKubuszok do you know how to use the compile time reflection to unpack generic types like above? All I have to work with is the generic type decelerations above as strings. Any pointers or search terms you think would be helpful is appreciated, I am failing hard on Google. – speak Apr 02 '20 at 13:30
  • 1
    Is `String` fully known in compile-time? If it is, then it could be used by macro. Through if type is known at compile-time String is literally the works way of instantiating things. – Mateusz Kubuszok Apr 02 '20 at 19:05
  • 1
    @MateuszKubuszok *"Runtime reflection don't see type parameters."* Actually runtime reflection sees type parameters if they are persisted from compile time (e.g. with `TypeTag`s). – Dmytro Mitin Sep 18 '20 at 03:09
  • 1
    @DmytroMitin true, it was simplification. It should have been "Runtime reflection cannot extract exact type parameter just from `Class` as there you have only the upper bound (`<:`) which is usually just `java.lang.Object`". You can pass that information but you have to reify these type parameters into some object from which you could retrieve them (`Class`, `ClassTag`, etc). – Mateusz Kubuszok Sep 18 '20 at 06:54
  • @speak Does my answer work for you? – Dmytro Mitin Sep 26 '20 at 09:18

1 Answers1

0

It seems you want to parse types

import scala.reflect.runtime
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox
val tb = ToolBox(runtime.currentMirror).mkToolBox()

def parseType(typ: String): Type = 
  tb.typecheck(tb.parse(s"type T = $typ"), mode = tb.TYPEmode) match {
    case q"type T = $tp" => tp.tpe
  }

val typ1 = "com.company.util.Type[com.company.controller.Response]"
val typ2 = "com.company.util.Type[scala.collection.Seq[com.company.controller.Response]]"
val typ3 = "com.company.util.Type[scala.collection.Seq[scala.Option[com.company.controller.Response]]]"

val tp1 = parseType(typ1)
tp1.typeConstructor // com.company.util.Type
tp1.typeArgs // List(com.company.controller.Response)

val tp2 = parseType(typ2) 
tp2.typeConstructor // com.company.util.Type
tp2.typeArgs.map(_.typeConstructor) // List(scala.collection.Seq)
tp2.typeArgs.map(_.typeArgs) // List(List(com.company.controller.Response))

val tp3 = parseType(typ3)
tp3.typeConstructor // com.company.util.Type
tp3.typeArgs.map(_.typeConstructor) // List(scala.collection.Seq)
tp3.typeArgs.map(_.typeArgs.map(_.typeConstructor)) // List(List(Option))
tp3.typeArgs.map(_.typeArgs.map(_.typeArgs)) // List(List(List(com.company.controller.Response)))
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66