views:

410

answers:

2

I have a few types that are like this

// a value that is aware of its key type (K)
Bar<K>
// something that deals with such values and keys
Foo<V extends Bar<K>, K>

how would one recreate Foo such that you could consume it in guice? the bit I'm stuck on is how to cross reference the K from Bar to the 2nd parameterised type of Foo.

so for example

WildcardType kType = Types.subtypeOf(Object.class);
WildcardType barType = Types.subtypeOf(Types.newParameterizedType(Bar.class, pipeKey));
ParameterizedType fooType = Types.newParameterizedType(Foo.class, pipelineableType, pipeKey);

really this seems wrong as it's basically

Foo<V extends Bar<? extends Object>, ? extends Object>

which is not the same thing as

Foo<V extends Bar<K>, K>

as in the latter case I know that K is a consistent type.

Any ideas?

Cheers

Matt

A: 

Guice's factory cannot build TypeVariable instances. You'll need to implement this interface directly as you need it.

Note that Guice doesn't allow bindings for types that aren't fully-qualified. For example, you can bind a Map<String, Integer> but you can't bind a Map<K, V>.

Jesse Wilson
+1  A: 

From the JavaDoc for Binder:

Guice cannot currently bind or inject a generic type, such as Set<E> all type parameters must be fully specified.

You can create bindings for Foo when K and V are bound. If you need to make bindings for Foo for more than one type of key, you can make a method that makes it easier to do these bindings. One way to do that is to create a method like this in your module:

<K, V extends Bar<K>> AnnotatedBindingBuilder<Foo<V, K>> bind(Class<K> keyType,
    Class<V> barType) {
  ParameterizedType bType = Types.newParameterizedType(Bar.class, keyType);
  ParameterizedType fType = Types.newParameterizedType(Foo.class, barType,
      keyType);

  @SuppressWarnings("unchecked")
  TypeLiteral<Foo<V, K>> typeLiteral =
      (TypeLiteral<Foo<V, K>>) TypeLiteral.get(fType);

  return bind(typeLiteral);
}

Then if you have these classes:

class StringValue implements Bar<String> {
  ...
}

class StringValueProcessor implements Foo<StringValue, String> {
  ...
}

You can create a binding like this:

bind(String.class, StringValue.class).to(StringValueProcessor.class);

...so that Guice could inject into a class like this:

static class Target {
  private final Foo<StringValue, String> foo;

  @Inject
  public Target(Foo<StringValue, String> foo) {
    this.foo = foo;
  }
}
NamshubWriter
interesting, I hadn't thought of binding in that way. What I'm really looking for the ability to search for a set of bindings that match some specification that may include some type params, e.g. all bindings that are Foo<V<String>,String> though I suppose in that case it's enough to simply look for Foo<?,String>... hmmm
Matt
Have you seen http://stackoverflow.com/questions/1100784/guice-spi-find-bindings-by-wildcard-types ?
NamshubWriter
yes that is my Q as well, I have implemented that in a way that works so far... but have been wondering how to extend it to a more general case. On reflection I'm not sure it's even needed as it's just a wildcard really. I need to think about it some more...
Matt