【问题标题】:Using nested enum types in Java在 Java 中使用嵌套枚举类型
【发布时间】:2011-11-09 22:33:31
【问题描述】:

我想到了一个涉及嵌套枚举的数据结构,因此我可以执行以下操作:

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

如果有方法声明:

someMethod(Drink type)
someOtherMethod(DrinkTypeInterface type)

那么我可以(恰当地)说:

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

这是我想出的:

public enum Drink {

    COFFEE("Coffee");

    private String groupName;

    private Drink(String groupName) {
        this.groupName = groupName;
    }

    public enum Coffee implements DrinkTypeInterface {

        COLUMBIAN("Columbian Blend"),
        ETHIOPIAN("Ethiopian Blend");

        private String label;

        private Coffee(String label) {
            this.label = label;
        }

        public String getLabel() {
            return this.label;
        }
    }

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

还有界面:

public interface DrinkTypeInterface {

    public String getLabel();
}

我想我只是想弄清楚在 Java 中做这类事情的最佳方法是什么,或者如果我需要编写一堆 if 语句来处理个人 Drink.values( )。有什么帮助吗?

【问题讨论】:

    标签: java enums nested-class


    【解决方案1】:

    考虑使用EnumSet 收集不同类型的Drink,建议here

    附录:作为一个具体的例子,下面的代码产生了所示的输出。

    咖啡:哥伦比亚混合咖啡 咖啡:埃塞俄比亚混合咖啡

    代码:

    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());
        }
    }
    

    【讨论】:

    • 感谢您的建议,绝对为我澄清了一些关于枚举的问题。我想我对自己的目标不是很清楚,并且太努力地想出一个有代表性的例子,而另一个回复设法解密了我的意思,所以我接受了那个。但我非常感谢您的反馈,并给了您一个支持 :)
    • 谢谢。实际上,我更喜欢 @MetroidFan2002 的类型令牌方法而不是 String 常量。
    • 我更新了我的示例以反映type-token pattern
    【解决方案2】:
    Drink.COFFEE.getGroupName();
    Drink.COFFEE.COLUMBIAN.getLabel();
    

    首先,您提供的示例代码在某种程度上违反了“demeter 定律”——因为 COLUMBIAN 实例字段仅用于检索标签。此外,使用这种结构,COLUMBIAN 必须是 COFFEE 枚举的一个实例,但我认为这不是你真正想要的。

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

    我从您的样本中收集到的是,您希望有一个枚举,其中包含实际饮料的“组类型”,然后每个人都有特定饮料类型的单独值。您的示例给出了 Coffee,但 Tea 应该也可以。

    问题在于您如何放置枚举。正如我之前所说,您必须让 COLUMBIAN 成为 COFFEE 枚举的实例,但这并不是构建它的最佳方式。

    问题是你有饮料,然后是咖啡/茶,然后是它们各自的类型。 但是,如果你仔细想想,虽然 HerbalTea 是一种茶,但它也是一种饮料 - 所以它不属于简单的 TEA 实例。

    但是,如果您将饮料 type 本身设置为枚举,您将得到您想要的,并且结构变得更加清晰。并且由于接口和委托的力量,饮料类型和饮料枚举都可以以相同的方式处理,如以下示例程序:

    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());
        }
    }
    

    这个程序的输出如下:

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

    那么,如果由于某种原因你不想在一个枚举中所有的饮料,那么我不明白你想要什么。在这种情况下,如果您确实具有跨越枚举的功能,请制作单独的 Coffee 和 Tea(以及其他)枚举并将接口应用于两个(或更多)枚举。但是,我认为您试图将它们分组。

    【讨论】:

    • 非常感谢您的详细解答!这让我足以知道我想做什么。为了澄清你最后所说的,基本上现在我有(另一位作者的创作)其中几个,例如茶,咖啡,...枚举。我希望能够将枚举类型本身视为一种类型,因此例如我可以有一个 List 并说出类似 for (Drink drink : List) { do something } 的内容。再次感谢!
    • 另外,在我的例子中,有一些方法是,例如 COFFEE 级别的方法,还有一些方法是,例如。哥伦比亚级别的方法。这就是为什么我没有尝试让它们都实现一个通用接口。
    【解决方案3】:

    你可以这样做:

    enum dogs {
        boxer, collie;
    }
    enum cats {
        siamese, tom
    }
    enum Animal {
        cat(cats.tom), dog(dogs.boxer);
        Animal(Enum e) {
            this.e = e;
        }
        Object[] subValues() {
            return e.getDeclaringClass().getEnumConstants();
        }
        final Enum e;
    }
    public class Main {
        public static void main(String[] args) {
            for (Animal animal : Animal.values()) {
                System.out.print(animal);
                for (Object o : animal.subValues())
                    System.out.print(" " + o);
                System.out.println();
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      我最近很好奇这是否可以令人满意地完成。这是我最终得到的解决方案,我认为它的 API 也更符合提问者最初想要的枚举树结构:

      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
      }
      

      【讨论】:

      • 问题要求嵌套枚举。您的示例仅显示了两个枚举。 Tea 是一个枚举,Coffies 是一个枚举。但是Drink 不是枚举。枚举的嵌套意味着 Drinks 本身不应该是接口,而是 Coffee 和 Tea 的枚举。
      猜你喜欢
      • 2011-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多