Injecting generic types

5,664 views
Skip to first unread message

Mirko Raner

unread,
Apr 18, 2012, 8:47:09 PM4/18/12
to google...@googlegroups.com
Hi all,
 
the Guice documentation for the Binder class states that "Guice cannot currently bind or inject a generic type, such as Set<E>; all type parameters must be fully specified."
Is there any remedy for this limitation in sight?
 
What I'm trying to do is the following:
A common source of unwanted coupling is introduced by using specific implementations of collection types or other generic types, for example
 
    Map<String, Integer> elements = new HashMap<String, Integer>();
 
which ties the code to a specific implementation (HashMap, in this case). Let's say that depending on my deployment scenario I might want to use GNU Trove classes like THashMap instead.
However, rather than hard-coding the implementing class in my code, I would like to write
 
    @Inject Map<String, Integer> elements;
 
and have Guice take care of the rest. I obviously don't want to create bindings specifically for maps from String to Integer, but for maps in general.
There might also be additional annotations to specify implementation preferences, for example
 
    @Inject @Identity Map<String, Integer> elements; // requires IdentityHashMap or similar
    @Inject @Synchronized @Linked Map<String, Integer> elements; // requires a synchronized LinkedHashMap or similar
 
Is support for features like this planned for a future release of Guice? Or is there a way to do this already?
 
Thanks,
 
Mirko
 

Hilco Wijbenga

unread,
Apr 18, 2012, 9:28:44 PM4/18/12
to google...@googlegroups.com

Can't you simply do MyMap extends Map<String, Integer> and inject
that? Or create a wrapper with a getter and inject an instance of that
wrapper? Sure, it's a bit more work but how many different maps do you
need?

In fact, you should probably use MyMap directly (without extending
Map) so you can limit its API to exactly what you need. The exact map
implementation that it uses is then really just an implementation
detail (it need not even implement Map<String, Integer>).

Reinhard Nägele

unread,
Apr 19, 2012, 6:30:27 AM4/19/12
to google...@googlegroups.com
This is, of course, possible. Look at http://code.google.com/p/google-guice/wiki/FrequentlyAskedQuestions under "How to inject class with generic type?"

Thomas Broyer

unread,
Apr 19, 2012, 6:57:26 AM4/19/12
to google...@googlegroups.com


On Thursday, April 19, 2012 12:30:27 PM UTC+2, Reinhard Nägele wrote:
This is, of course, possible. Look at http://code.google.com/p/google-guice/wiki/FrequentlyAskedQuestions under "How to inject class with generic type?"

Except that Mirko said: “I obviously don't want to create bindings specifically for maps from String to Integer, but for maps in general.”

Thomas Suckow

unread,
Apr 19, 2012, 3:15:07 AM4/19/12
to google...@googlegroups.com
This should work if you make the binding using typeLiteral.

TypeLiteral<List<String>> list = new TypeLiteral<List<String>>() {;}
http://google-guice.googlecode.com/git/javadoc/com/google/inject/Binder.html#bind(com.google.inject.TypeLiteral<T>)

Scala users should seriously consider using:
https://github.com/Deathbobomega/scala-guice
It will hide this pain inside Scala magic.

--
Thomas Suckow

> --
> You received this message because you are subscribed to the Google Groups "google-guice" group.
> To post to this group, send email to google...@googlegroups.com.
> To unsubscribe from this group, send email to google-guice...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/google-guice?hl=en.
>

Thomas Suckow

unread,
Apr 19, 2012, 1:27:59 PM4/19/12
to google...@googlegroups.com
Am I mistaken in believing this is what Mirko is looking for? One
issue is that multiple annotations do not appear to be supported.

Pseudo Code:

TypeLiteral<Map<String, Integer>> literal = new
TypeLiteral<Map<String, Integer>>() {;}

bind(literal).annotatedWith(Identity.class).to(IdentityHashMap.class)
bind(literal).annotatedWith(SynchronizedLinked.class).to(SynchronizedLinkedHashMap.class)

--
Thomas Suckow

> --
> You received this message because you are subscribed to the Google Groups
> "google-guice" group.

> To view this discussion on the web visit
> https://groups.google.com/d/msg/google-guice/-/YfM_xhqTIjcJ.

Mirko Raner

unread,
Apr 19, 2012, 2:04:16 PM4/19/12
to google...@googlegroups.com
Thanks, Thomas B., that was exactly my point! :-) Sorry that my original question didn't really make it clear what I had in mind.
To clarify what I'm after: I would like to create a binding for the generic version of a type, not for a concrete parameterized type.
If you look through your source code on a real-world project, you will probably find hundreds of uses of java.util.Map, all of them parameterized to different key and value type parameters. Right now, using Hilco and Thomas S.'s suggestion, I would have to find all combinations of key/value types and provide separate bindings for them. I don't want to create bindings that are specific to the key/value types; I want to create a binding for the general "Map" concept and specify a Map implementation that is not bound to specific key/value types but is still generic.
 
The closest I can come expressing this in Guice is
 

    binder.bind(new TypeLiteral<Map<?,?>>(){}).to(new TypeLiteral<HashMap<?,?>>(){});

but that doesn't seem to work. If anyone knows how to achieve this kind of binding that would be great (though I'm not very hopeful, as the Binder documentation seems to say that it's not possible, if I understand it correctly).

Thanks for any insights!

Mirko

 

Colin Decker

unread,
Apr 19, 2012, 2:16:02 PM4/19/12
to google...@googlegroups.com
Personally, I don't think what you're trying to do (at least with the Maps) is a very good idea. If you need an IdentityHashMap, create an IdentityHashMap. You're basically trying to specify the exact type of Map you want with annotations. What's that accomplishing for you? Do you really need to be able to easily substitute a different type of Map for testing or some such? In general, I'd avoid injecting things like data structures that should be internal details of your class.

In any case, Guice doesn't support this.

-- 
Colin


--
You received this message because you are subscribed to the Google Groups "google-guice" group.
To view this discussion on the web visit https://groups.google.com/d/msg/google-guice/-/kmMO1_VDZ4oJ.

Mirko Raner

unread,
Apr 20, 2012, 2:08:58 PM4/20/12
to google-guice
Your suggestion is still specific to a particular set of type
parameters (<String, Integer>). I would have to add bindings for each
set of type parameters that I intend to use. I'm more thinking of a
<?, ?> binding (not really, but that's the closest Java syntax allows
you to express). See my other response from April 19th.

On Apr 19, 10:27 am, Thomas Suckow <tsuc...@gmail.com> wrote:
> Am I mistaken in believing this is what Mirko is looking for? One
> issue is that multiple annotations do not appear to be supported.
>
> Pseudo Code:
>
> TypeLiteral<Map<String, Integer>> literal = new
> TypeLiteral<Map<String, Integer>>() {;}
>
> bind(literal).annotatedWith(Identity.class).to(IdentityHashMap.class)
> bind(literal).annotatedWith(SynchronizedLinked.class).to(SynchronizedLinked­HashMap.class)

Mirko Raner

unread,
Apr 20, 2012, 2:29:29 PM4/20/12
to google-guice
Thanks, Colin. You are raising a good point here, but the Maps were
just an example (and maybe a bad one). I guess it's debatable whether
the coupling to data structure implementations is a serious issue (but
it's still coupling, and I can imagine scenarios where it would cause
problems). I understand your point that "@Inject @Synchronized
@Identity Map map;" is almost as specific as creating an synchronized
IdentityHashMap, but still the injected variant provides flexibility
to exchange the implementation for a somehow optimized (Trove classes,
etc.) or maybe instrumented implementation. You don't have that
flexibility if you hard-code IdentityHashMap.
I see your point regarding collections, so it's probably a good idea
to move away from that example. In my opinion, type casts are one of
the biggest problems of type safety in Java, and I use generics
whenever possible to get rid of type casts. So, we're not only talking
about collections here, but any sort of application-specific generics.
For example, some customized storage or message class that can (type-
safely) contain different types of things. Or some other pervasive
mechanism in your application that requires different implementations
during testing and runtime, or when using remote or local storage (or
when running with different memory footprints).
Anyway, thanks for confirming that this is currently not possible in
Guice. Do you know of any intrinsic technical reason why this cannot
be supported, or is this simply the way the injector works right now
(but could be supported)?
As I mentioned in my other post, I was able to specify a binding of a
generic (i.e., <?>) TypeLiteral for an interface to a generic
TypeLiteral for its implementation class, but the binding seemed not
to have any effect for the injector.

Thanks,

Mirko

Mirko Raner

unread,
Apr 20, 2012, 9:52:48 PM4/20/12
to google-guice
On Apr 19, 11:04 am, Mirko Raner <mi...@raner.ws> wrote:
> The closest I can come expressing this in Guice is
>
>     binder.bind(new TypeLiteral<Map<?,?>>(){}).to(new
> TypeLiteral<HashMap<?,?>>(){});
>
> but that doesn't seem to work.

Sorry, another clarification -- apparently I'm really good at
confusing everyone ;-) :

The above binding of course DOES work for injections like @Inject
Map<?,?> map (with a wildcarded type), which makes sense. In line with
Java's type system this binding does not match any other injected
types (like Map<String, String>).
What I would really like to bind is Map<K,V> (with K and V being
unbound type parameters), but there is really no way to express that.
I tried binding raw types (i.e., TypeLiteral<Map> to
TypeLiteral<HashMap>, but (as expected) that binding only works for
raw types at the injection point.
I guess as part of solving this one would have to introduce special
"any" or "unbound" classes to be used for specifying the bindings: for
example, TypeLiteral<Map<Any,Any>>, or TypeLiteral<List<Unbound>>, or
maybe even something like TypeLiteral<Map<_,_>> or TypeLiteral<Map<$,
$>>.

I think at this point we all agree that Guice does not support this
use case. The follow-up questions, I guess, are: can it be implemented
and is it worth it?

Thomas Broyer

unread,
Apr 21, 2012, 5:13:02 AM4/21/12
to google...@googlegroups.com
Looking at the docs [1], it seems like you could do it using a TypeListener [2]. I mean, if one can do AssistedInject and Multibinding, there's no reason you couldn't do "generic binding". I have absolutely no idea how you'd implement it though.

Reply all
Reply to author
Forward
0 new messages