Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is binding to a TypeLiteral a good or bad practice in google guice

Google guice uses new TypeLiteral<C<T>>() {} to overcome the fact that we cannot use C<T>.class.

Now it is common to the following:

bind(new TypeLiteral<C<T>>() {}).to(MyCSubclassTypedToT.class);

Imagine a different scenario however. We have a generic interface, that we want to inject and the implementation we have is be provided by a generic class.

Guice allows you to do this like this:

bind(new TypeLiteral<MyGenericInterface<T>>() {}).to(new TypeLiteral<MyGenericClass<T>>() {});

Another way of doing this would be to extend MyGenericClass like this:

MyTypedClass extends MyGenericClass<T>

and then bind it like this:

bind(MyGenericInterface<T>>() {}).to(MyTypedClass.class);

If MyGenericInterface is injected a lot (albeit with different types), and every time I do inject it I use MyGenericClass, the latter approach leads to overly verbose code. Hence I'm leaning towards using the former.

I would be very keen to hear other peoples opinion about the use of a TypeLiteral in the to clause of a guice binding. I'm afraid that I might a bit to short sited and thus don't see the pitfalls of this approach.

like image 775
velocipedist Avatar asked Apr 26 '12 10:04

velocipedist


2 Answers

In this case, using a TypeLiteral for the generic implementation strikes me as being much better.

Let me phrase it this way: "Would the subclass MyTypedClass exist if you weren't using Guice?". An essential guideline of Guice is: you shouldn't have to alter your implementation classes to fit the DI framework (well, with the exception of things like the @Inject annotation).

And what do you gain from subclassing the concrete-yet-generic class? One big loss is that you have to duplicate MyGenericClass's constructor in all the subclasses. All in all, it seems like extra code without much gain.

All-in-all, there's generally nothing wrong with using a TypeLiteral. And if it's in the .to(...) portion of the binding clause (as opposed to bind(...), it won't even affect the publicly-visible portion of your Module's bindings, so I don't think there's much to worry about.

like image 71
Andrew McNamee Avatar answered Nov 05 '22 21:11

Andrew McNamee


There are usually two scenarios:

  1. You want to bind all implementation of MyGenericInterface to MyGenericClass
  2. You want to bind implementation of MyGenericInterface base on its type

For the first scenario, bind(MyGenericInterface).to(MyGenericClass); will be enough, simper and easier to understand.

For the 2nd scenario, you will need to bind implementation of a specific class to a specific implementation, which TypeLiteral will come into play.

Furthermore, the code in your question is not clear that whether T is a actual class, or a generic type. If it's a generic type,

bind(new TypeLiteral<MyGenericInterface<T>>() {}).to(new TypeLiteral<MyGenericClass<T>>() {});

and

MyTypedClass extends MyGenericClass<T>

won't compile, as some actual class is not provided.

like image 28
wyz Avatar answered Nov 05 '22 23:11

wyz