Using nested enum types in Java

Drink.COFFEE.getGroupName();
Drink.COFFEE.COLUMBIAN.getLabel();

First off, that sample code you gave violates the "law of demeter" somewhat - as the COLUMBIAN instance field is only used to retrieve the label. Also, with that structure, COLUMBIAN has to be an instance of the COFFEE enum, but I don't think that's what you're really going for here.

someMethod(Drink type)
someOtherMethod(DrinkTypeInterface type)

someMethod(Drink.COFFEE)
someOtherMethod(Drink.COFFEE.COLUMBIAN)

What I'm gathering from what your sample is, is that you want to have an enumeration that contains a "group type" of what the actual drink is, and then each one has individual values for the specific type of drink. Your example gives Coffee, but Tea should work just as well.

The problem is how you've placed your enumerations. As I said before, you'd have to make COLUMBIAN an INSTANCE of the COFFEE enumeration, but that's not really the best way to structure this.

The problem is that you've got Drink, then Coffee/Tea, and then their individual types. But, if you think about it, although HerbalTea IS A Tea, it is also a DRINK - so it doesn't belong as simply an instance of a TEA.

But, if you make the drink type an enum in and of itself, you get what you wanted, and the structure becomes clearer. And due to interfaces and the power of delegation, both the drink type and the drink enum can be processed in the same manner, as with the following example program:

public final class DrinkEnumExample {

    public interface DrinkTypeInterface {

        String getDisplayableType();
    }

    public static enum DrinkType implements DrinkTypeInterface {

        COFFEE("Coffee"), TEA("Tea");
        private final String type;

        private DrinkType(final String type) {
            this.type = type;
        }

        public String getDisplayableType() {
            return type;
        }
    }

    public static enum Drink implements DrinkTypeInterface {

        COLUMBIAN("Columbian Blend", DrinkType.COFFEE),
        ETHIOPIAN("Ethiopian Blend", DrinkType.COFFEE),
        MINT_TEA("Mint", DrinkType.TEA),
        HERBAL_TEA("Herbal", DrinkType.TEA),
        EARL_GREY("Earl Grey", DrinkType.TEA);
        private final String label;
        private final DrinkType type;

        private Drink(String label, DrinkType type) {
            this.label = label;
            this.type = type;
        }

        public String getDisplayableType() {
            return type.getDisplayableType();
        }

        public String getLabel() {
            return label;
        }
    }

    public DrinkEnumExample() {
        super();
    }

    public static void main(String[] args) {
        System.out.println("All drink types");
        for (DrinkType type : DrinkType.values()) {
            displayType(type);
            System.out.println();
        }
        System.out.println("All drinks");
        for (Drink drink : Drink.values()) {
            displayDrink(drink);
            System.out.println();
        }
    }

    private static void displayDrink(Drink drink) {
        displayType(drink);
        System.out.print(" - ");
        System.out.print(drink.getLabel());
    }

    private static void displayType(DrinkTypeInterface displayable) {
        System.out.print(displayable.getDisplayableType());
    }
}

The output of this program is as follows:

All drink types 
Coffee 
Tea 
All drinks 
Coffee - Columbian Blend 
Coffee - Ethiopian Blend
Tea - Mint 
Tea - Herbal 
Tea - Earl Grey

Now then, if for some reason you didn't want all your drinks in a single enum, then I didn't understand what you were going for. In that case, if you do have functionality that spans the enums, make separate Coffee and Tea (and whatever) enumerations and apply the interface on both (or more) enumerations. But, I think you were trying to group them like this.


Consider using EnumSet to collect different types of Drink, as suggested here.

Addendum: As a concrete example, the code below produces the output shown.

Coffee: Columbian Blend
Coffee: Ethiopian Blend

Code:

public static enum DrinkType {

    COFFEE("Coffee"), TEA("Tea");
    private final String displayName;

    private DrinkType(final String displayName) {
        this.displayName = displayName;
    }

    public String getDisplayName() {
        return displayName;
    }
}

public enum Drink {

    COLUMBIAN(DrinkType.COFFEE, "Columbian Blend"),
    ETHIOPIAN(DrinkType.COFFEE, "Ethiopian Blend"),
    MINT_TEA(DrinkType.TEA, "Mint"),
    HERBAL_TEA(DrinkType.TEA, "Herbal"),
    EARL_GREY(DrinkType.TEA, "Earl Grey");
    public static Set<Drink> coffees = EnumSet.of(COLUMBIAN, ETHIOPIAN);
    public static Set<Drink> teas = EnumSet.range(MINT_TEA, EARL_GREY);
    private String groupName;
    private String drinkName;

    private Drink(DrinkType type, String drinkName) {
        this.groupName = type.getDisplayName();
        this.drinkName = drinkName;
    }

    public String getGroupName() {
        return this.groupName;
    }

    public String getDrinkName() {
        return drinkName;
    }
}

public static void main(String... args) {
    for (Drink d : Drink.coffees) {
        System.out.println(d.getGroupName() + ": " + d.getDrinkName());
    }
}