Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guice, binding to enum

Tags:

java

enums

guice

I am having hard time figuring out how to bind interface to enum using Guice.

public interface Snack {
    public int caloriesCount();
}

public enum Chocolate implements Snack {
    MILK(20),
    DARK(5),
    WHITE(10);

    private int calCount;

    private Chocolate(int calCount) {
        this.calCount = calCount;
    }

    @Override
    public int caloriesCount() {
        return calCount;
    }   
}

If I try: bind(Snack.class).to(Chocolate.class);

I get No implementation for Chocolate was bound

I understand that instead of trying to bind to the enum I should bind to the set of that enum's values, but it defies me how that can be accomplished. Appreciate any hints. Thanks, Maciek

like image 928
Maciek Avatar asked Sep 19 '25 00:09

Maciek


1 Answers

Binding to an enum class like that is impossible, because Guice cannot create its own instance. Here, the default behavior would be to create a new instance of Chocolate, which doesn't make a lot of sense—every possible instance of an enum must be declared at compile time within the enum itself, and Guice won't pick an arbitrary existing one for you.

You might want to do one of these three things instead:

  1. Bind Snack to a particular type of Chocolate.

    bind(Snack.class).toInstance(Chocolate.DARK);
    
  2. Bind Snack to Chocolate, and Chocolate to a particular type of Chocolate. This is like the above, but now you can ask for Chocolate directly and get an instance, instead of only being able to ask for a Snack.

    bind(Snack.class).to(Chocolate.class);
    bind(Chocolate.class).toInstance(Chocolate.DARK);
    
  3. Allow developers to inject a Set<Snack>, which happens to return all Chocolate instances.

    // manual wildcard required so we don't get an ImmutableSet<Chocolate>
    Set<Snack> setOfSnacks = ImmutableSet.<Snack>copyOf(Chocolate.values());
    
    // bind a Set<Snack> specifically
    bind(new TypeLiteral<Set<Snack>>() {}).toInstance(setOfSnacks);
    

    You can use any Set implementation, but because all toInstance bindings are implicitly singletons, you can reduce some risk of modification if you use Guava's ImmutableSet or at least Collections.unmodifiableSet. If this is close to what you want, you may be interested in learning about the optional Multibindings plugin.

    Of course you could also write your own Provider<Set<Snack>> or a method that @Provides Set<Snack>, thereby getting a new set instance every time...but if you expect to use this across your app it may make more sense to share one immutable copy.

like image 117
Jeff Bowman Avatar answered Sep 21 '25 12:09

Jeff Bowman