Generic bindings

63 views
Skip to first unread message

kel...@gmail.com

unread,
Jul 1, 2014, 6:41:00 PM7/1/14
to google...@googlegroups.com
Suppose I have the following:

public class Foo<T> {
    private final Class<T> tClass;
    @Inject public Foo(Class<T> tClass) { this.tClass = tClass; }
}

I've heard that if tClass was instead a TypeLiteral, then I'd basically have Foo<Integer>, Foo<String>, etc. for free.  But suppose Foo is a third-party class, and I wish to duplicate its effects.  Since Class has no automatic binding, I'm pretty much stuck with

bind(new TypeLiteral<Class<Integer>>(){}).toInstance(Integer.class);

Which has the drawback of, well, having to bind types that I know will be used, leaving out unintended uses and such.

Now, it's probably possible, and even trivial, to implement a Provider<Class<T>> for which, given a TypeLiteral<T> (where T is a non-generic type), returns a Class<T>.  However, I'm still stuck with binding it in a non-generic manner.

Is there a workaround for this, so that I essentially have Foo<Integer>, Foo<String>, etc., for free?

Also, would it be the same if instead tClass was MyCustomTypeTokenClass (ie. not a TypeLiteral, but convertible from it)?

Tavian Barnes

unread,
Jul 11, 2014, 2:07:30 PM7/11/14
to google...@googlegroups.com
Guice can't do that automatically because no such thing as Class<List<String>>, for example.

You could accomplish MyCustomTypeTokenClass<T> with a just-in-time bindings.  Just let it take a TypeToken<T> in its constructor and do the conversion there.

Tim Boudreau

unread,
Jul 25, 2014, 3:02:59 AM7/25/14
to google...@googlegroups.com

That’s a pretty fundamental limitation of type erasure and generics when it comes to things like Guice.

Maybe reconsider your design a little bit - usually these things can be solved without too much pain.  A few things can help:

1. A handy generics trick, if you say, need to instantiate a Bar<T> for a Foo<T> is 

public Thing (Foo<?> foo) {
   createBar(foo);
}

private <T> Bar<T> createBar(Foo<T> foo) {
   return new Bar<T>(foo);
}

which lets the compiler still enforce that T must agree for Foo and Bar without having to care what T is.

2.  Similarly, if you keep a Class object at runtime (and you should if it’s extensible), you can 

private <T> T giveItAType (Class<T> type, Object o) {
   return type.cast(o);
}

to enforce type safety (if somewhere you absolutely have to cast Foo<?> to Foo<T> you want this, and it also does for you what the compiler cannot).

3.  In Acteur - https://github.com/timboudreau/acteur - it is normal for clients of the framework to add their own types for injection, and then get those injected into later Acteurs in the chain, so I wrote a custom reentrant scope that you enter with an Object[] making any of those objects injectable in later calls while in the scope (mostly the usual ThreadLocal magic), and then requiring an @ImplicitBindings(Type1.class, Type2.class) on the application to have those bound on startup.

The thing to realize is that, if you’re writing something extensible where clients will add types that your code knows nothing about, is that your code doesn’t need to touch objects of those types so much as just provide a type-safe pipeline for passing them around between chunks of code that will know what to do with them.

-Tim

Reply all
Reply to author
Forward
0 new messages