Jonathan
Jonathan

Reputation: 7604

How do I bind a single implementation to multiple generified interfaces in Guice?

I have a set of 15-20 classes that depend on a class that implements generic interface with bound type parameters. The interface looks like this:

interface Handler<F extends Foo, T extends Foo> {
    T handleFoo(String methodName, F myFoo);
}

And a single implementation of Handler that uses reflection to deal with all the specialised cases of Foo, because they're all "similar but different".

class ConcreteHandler<Foo, Foo> implements Handler<Foo, Foo> {
    T handleFoo(String methodName, F foo) {
        // do your thing
    }
}

I'd like to be able to use this class in a type-safe way in its collaborators, like:

@Inject ACollaborator(Handler<Bar, Baz> barHandler) {...}

where Bar and Baz both extend Foo. Unfortunately, Guice complains that Handler<Bar, Baz> is not bound when I try this in my module:

bind(new TypeLiteral<Handler<? extends Foo, ? extends Foo>>() {}).to(ConcreteHandler.class);

I have also tried the following, which causes a compiler error because ConcreteHandler is not a subclass of Handler<Bar, Baz>:

bind(new TypeLiteral<Handler<Bar, Baz>>() {}).to(ConcreteHandler.class);

I have so many of these Collaborator objects that I am not keen on the thought of implementing Handler for each combination of types, and putting in 20 separate binding statements for them.

How do I accomplish what I'm attempting with Guice, that is binding a single concrete implementation to a single interface with different generic parameters?

Upvotes: 3

Views: 651

Answers (1)

Fred
Fred

Reputation: 101

You can manage to do this by creating the TypeLiteral representing the binding target, probably with a raw type involved.

You'll see an example of how to do this in this question: Reconstructing generic types at runtime with Guice via Types and TypeLiterals

Basically what you'll needs is something to construct the Key of each Handler<> you want bound.

Then you can say something like:

bindHandler(Bar.class, Baz.class);

Which will:

  1. create a TypeLiteral<ConcreteFoo<Bar, Baz>>
  2. create a TypeLiteral<Foo<Bar, Baz>>
  3. bind the latter to the former

Also I think in your example ConcreteHandler should implement Handler[?]

Upvotes: 2

Related Questions