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());
}
}
I was recently curious myself if this could be done somewhat satisfactorily. This is the solution I ended up with, whose API I think also closer matches the tree structure of enums that the asker originally wanted:
public interface Drink {
String groupName();
String label();
enum Coffee implements Drink {
COLUMBIAN("Columbian Blend"),
ETHIOPIAN("Ethiopian Blend");
private final String label;
Coffee(String label) {
this.label = label;
}
@Override
public String groupName() {
return "Coffee";
}
@Override
public String label() {
return label;
}
}
enum Tea implements Drink {
MINT("Mint"),
HERBAL("Herbal"),
EARL_GREY("Earl Grey");
private final String label;
Tea(String label) {
this.label = label;
}
@Override
public String groupName() {
return "Tea";
}
@Override
public String label() {
return label;
}
}
}
public static void main(String[] args) {
Drink choice = Drink.Tea.EARL_GREY;
System.out.println(choice.groupName()); // Tea
System.out.println(choice.label()); // Earl Grey
}