Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@Provides with @Named doesn't work for supertype declared variables

Tags:

java

guice

I'm currently exploring Guice capabilities and faced a strange behaviour - when I declare variable as

@Inject
@Named("dragon")
Dragon dragon2;

injection works as expected, but when I want to declare dragon2 as interface (it implements Creature), i. e.

@Inject
@Named("dragon")
Creature dragon2;

I get an error

No implementation for warlock.rincewind.creatures.Creature annotated with @com.google.inject.name.Named(value=dragon) was bound.

Here's my provider method:

@Named("dragon")
@Provides
public Dragon providesDragon() {
    Dragon d = new Dragon("Morkeleb");
    return d;
}

I know, there are lots of different ways to overcome this (the simplest one is change provides return type to Creature) but I'm trying to get the reason for such limitation.

like image 469
skwisgaar Avatar asked Apr 20 '15 11:04

skwisgaar


1 Answers

Consider, for example:

interface Creature { }

class Dragon implements Creature { }
class Dog implements Creature { }

public class Kennel {
    @Inject @Named("foo") Creature c;
}

public class KennelModule extends Abstract Module {
    @Override
    protected void configure() {
        bind(Dragon.class).annotatedWith(Names.named("foo"));
        bind(Dog.class).annotatedWith(Names.named("foo"));
    }
}

How would your injection know whether to bind Dragon or Dog here?

It can't! Injection keys are not covariant. What you need to do instead is, specify the subclass that you want to inject, and bind it to the interface or superclass that you are injecting to. In other words:

public class KennelModule extends Abstract Module {
    @Override
    protected void configure() {
        bind(Creature.class).annotatedWith(Names.named("foo").to(Dragon.class);
    }
}

This will give you the behavior you want. You can also still bind injections for Dragon specifically, of course.

like image 94
durron597 Avatar answered Sep 21 '22 17:09

durron597