Search code examples
scalareflectionguice

How to reflectively parameterise a generic type in Scala?


How can I implement the following psuedocode in Scala using reflection?

I require this for the purposes of looking-up a generic type from Guice:

trait Foo[A]
class FooInt extends Foo[Int]
class FooString extends Foo[String]

bind(new TypeLiteral<Foo<Int>>() {}).to(FooInt.class);

def fooTypeLiteral(paramA: Class[_]): TypeLiteral[_] = ???

val foo = injector.getInstance(fooTypeLiteral(classOf[Int])
// foo: FooInt

Note: I do not have access to the type of A at compile time, hence the _. The entire solution needs to be performed reflectively (e.g. I cannot have parameterizeFoo[A : ClassTag](...)).


Solution

  • You could try to create a ParameterizedType and pass it to the factory method of the TypeLiteral:

    def fooTypeLiteral(paramA: Class[_]): TypeLiteral[_] = {
      TypeLiteral.get(new java.lang.reflect.ParameterizedType() {
        def getRawType = classOf[Foo[_]]
        def getOwnerType = null
        def getActualTypeArguments = Array(paramA)
      })
    }
    

    If you have only a finite number of Foo implementations, you could try this:

    trait Foo[A]
    class FooInt extends Foo[Int]
    class FooString extends Foo[String]
    
    val TLFI = new TypeLiteral[Foo[Int]](){}
    val TLFS = new TypeLiteral[Foo[String]](){}
    
    bind(TLFI).to(FooInt.class);
    bind(TLFS).to(FooString.class);
    
    def fooTypeLiteral(c: Class[_]): TypeLiteral[_] = {
      if (c == classOf[Int]) TLFI
      else if (c == classOf[String]) TLFS
      else throw new Error
    }