【问题标题】:Why would an Enum implement an Interface?为什么枚举要实现接口?
【发布时间】:2011-02-12 04:02:12
【问题描述】:

我刚刚发现 Java 允许枚举实现接口。什么是一个好的用例?

【问题讨论】:

标签: java enums


【解决方案1】:

这是一个示例(在 Effective Java 2nd Edition 中可以找到类似/更好的示例):

public interface Operator {
    int apply (int a, int b);
}

public enum SimpleOperators implements Operator {
    PLUS { 
        int apply(int a, int b) { return a + b; }
    },
    MINUS { 
        int apply(int a, int b) { return a - b; }
    };
}

public enum ComplexOperators implements Operator {
    // can't think of an example right now :-/
}

现在获取简单 + 复杂运算符的列表:

List<Operator> operators = new ArrayList<Operator>();

operators.addAll(Arrays.asList(SimpleOperators.values()));
operators.addAll(Arrays.asList(ComplexOperators.values()));

所以在这里你使用一个接口来模拟可扩展的枚举(如果不使用接口是不可能的)。

【讨论】:

  • 复杂的运算符可以是“pow”等。
  • 第一次看到 Enum 语法(成员后面有大括号),我已经用 Java 编程 6 年了。直到。
【解决方案2】:

枚举不仅仅必须表示被动集(例如颜色)。它们可以表示具有功能的更复杂的对象,因此您可能希望向这些对象添加更多功能 - 例如您可能有PrintableReportable 等接口以及支持这些接口的组件。

【讨论】:

    【解决方案3】:

    这里几个人给出的Comparable 示例是错误的,因为Enum 已经实现了它。你甚至不能覆盖它。

    一个更好的例子是拥有一个定义数据类型的接口。你可以有一个枚举来实现简单的类型,也可以有普通的类来实现复杂的类型:

    interface DataType {
      // methods here
    }
    
    enum SimpleDataType implements DataType {
      INTEGER, STRING;
    
      // implement methods
    }
    
    class IdentifierDataType implements DataType {
      // implement interface and maybe add more specific methods
    }
    

    【讨论】:

    • 是的。很奇怪!!。 Enum 已经实现了 Comparable 接口并且不允许覆盖。我查找了枚举类的 java 源代码,但找不到 compareTo 实现。谁能告诉我在 compareTo 方法和 ENUM 中比较了什么?
    • @AKh 它比较序数,这意味着自然排序是枚举常量在源文件中的顺序。
    • 枚举变量是最终的,所以比较发生与 == ,对吧?
    • @djangofan 是的。
    【解决方案4】:

    有一个我经常使用的案例。我有一个带有静态方法的IdUtil 类来处理实现一个非常简单的Identifiable 接口的对象:

    public interface Identifiable<K> {
        K getId();
    }
    
    
    public abstract class IdUtil {
    
        public static <T extends Enum<T> & Identifiable<S>, S> T get(Class<T> type, S id) {
            for (T t : type.getEnumConstants()) {
                if (Util.equals(t.getId(), id)) {
                    return t;
                }
            }
            return null;
        }
    
        public static <T extends Enum<T> & Identifiable<S>, S extends Comparable<? super S>> List<T> getLower(T en) {
            List<T> list = new ArrayList<>();
            for (T t : en.getDeclaringClass().getEnumConstants()) {
                 if (t.getId().compareTo(en.getId()) < 0) {
                     list.add(t);
                }
            }
            return list;
        }
    }
    

    如果我创建一个Identifiable enum

        public enum MyEnum implements Identifiable<Integer> {
    
            FIRST(1), SECOND(2);
    
            private int id;
    
            private MyEnum(int id) {
                this.id = id;
            }
    
            public Integer getId() {
                return id;
            }
    
        }
    

    然后我可以通过它的id 得到它:

    MyEnum e = IdUtil.get(MyEnum.class, 1);
    

    【讨论】:

    • 正是我需要的!
    【解决方案5】:

    由于枚举可以实现接口,它们可以用于严格执行单例模式。尝试将标准类设为单例允许...

    • 考虑使用反射技术将私有方法公开为公共方法的可能性
    • 用于从单例继承并用其他方法覆盖单例的方法

    枚举作为单例有助于防止这些安全问题。这可能是让 Enums 充当类并实现接口的促成原因之一。只是猜测。

    更多讨论请参见https://stackoverflow.com/questions/427902/java-enum-singletonSingleton class in java

    【讨论】:

    • for inheriting from your singleton and overriding your singleton's methods with something else。您可以使用final class 来防止这种情况发生
    【解决方案6】:

    它是可扩展性所必需的——如果有人使用您开发的 API,您定义的枚举是静态的;它们不能被添加或修改。但是,如果你让它实现一个接口,使用 API 的人可以使用相同的接口开发他们自己的枚举。然后,您可以使用枚举管理器注册此枚举,该管理器将枚举与标准接口结合在一起。

    编辑:@Helper 方法有一个完美的例子。考虑让其他库定义新的运算符,然后告诉管理器类“嘿,这个枚举存在——注册它”。否则,您只能在自己的代码中定义运算符 - 没有可扩展性。

    【讨论】:

      【解决方案7】:

      枚举只是伪装的类,所以在大多数情况下,你可以用一个类做的任何事情都可以用一个枚举来做。

      我想不出一个枚举不应该实现接口的原因,同时我也想不出一个很好的理由。

      我会说,一旦你开始向枚举添加接口或方法之类的东西,你真的应该考虑将它变成一个类。当然,我确信做非传统枚举的事情是有道理的,而且由于限制是人为的,我赞成让人们在那里做他们想做的事。

      【讨论】:

      • "...所以在大多数情况下,你可以用一个类做任何你可以用一个枚举做的事情”,但我不能说我的枚举是从一个抽象类继承的。所以,是的,强调“大部分”。
      • 那是因为枚举都扩展了java.lang.Enum,而Java类只能从一个类扩展。
      【解决方案8】:

      上面提到策略的帖子没有足够强调使用枚举的策略模式的轻量级实现会给你带来什么:

      public enum Strategy {
      
        A {
          @Override
          void execute() {
            System.out.print("Executing strategy A");
          }
        },
        
        B {
          @Override
          void execute() {
            System.out.print("Executing strategy B");
          }
        };
      
        abstract void execute();
      }
      

      您可以将所有策略放在一个地方,而无需为每个策略单独编译单元。只需:

      Strategy.valueOf("A").execute();
      

      让 java 读起来几乎就像一种美味的松散类型语言!

      【讨论】:

        【解决方案9】:

        例如,如果您有一个 Logger 枚举。然后你应该在接口中有调试、信息、警告和错误等记录器方法。它使您的代码松散耦合。

        【讨论】:

          【解决方案10】:

          最常见的用法是将两个枚举的值合并到一个组中并以类似方式对待它们。例如,查看如何join Fruits and Vegatables

          【讨论】:

            【解决方案11】:

            对我来说,将枚举与接口一起使用的最佳用例之一是谓词过滤器。这是弥补 apache 集合缺乏类型的一种非常优雅的方法(如果不能使用其他库)。

            import java.util.ArrayList;
            import java.util.Collection;
            
            import org.apache.commons.collections.CollectionUtils;
            import org.apache.commons.collections.Predicate;
            
            
            public class Test {
                public final static String DEFAULT_COMPONENT = "Default";
            
                enum FilterTest implements Predicate {
                    Active(false) {
                        @Override
                        boolean eval(Test test) {
                            return test.active;
                        }
                    },
                    DefaultComponent(true) {
                        @Override
                        boolean eval(Test test) {
                            return DEFAULT_COMPONENT.equals(test.component);
                        }
                    }
            
                    ;
            
                    private boolean defaultValue;
            
                    private FilterTest(boolean defautValue) {
                        this.defaultValue = defautValue;
                    }
            
                    abstract boolean eval(Test test);
            
                    public boolean evaluate(Object o) {
                        if (o instanceof Test) {
                            return eval((Test)o);
                        }
                        return defaultValue;
                    }
            
                }
            
                private boolean active = true;
                private String component = DEFAULT_COMPONENT;
            
                public static void main(String[] args) {
                    Collection<Test> tests = new ArrayList<Test>();
                    tests.add(new Test());
            
                    CollectionUtils.filter(tests, FilterTest.Active);
                }
            }
            

            【讨论】:

              【解决方案12】:

              另一种可能性:

              public enum ConditionsToBeSatisfied implements Predicate<Number> {
                  IS_NOT_NULL(Objects::nonNull, "Item is null"),
                  IS_NOT_AN_INTEGER(item -> item instanceof Integer, "Item is not an integer"),
                  IS_POSITIVE(item -> item instanceof Integer && (Integer) item > 0, "Item is negative");
              
                  private final Predicate<Number> predicate;
                  private final String notSatisfiedLogMessage;
              
                  ConditionsToBeSatisfied(final Predicate<Number> predicate, final String notSatisfiedLogMessage) {
                      this.predicate = predicate;
                      this.notSatisfiedLogMessage = notSatisfiedLogMessage;
                  }
              
                  @Override
                  public boolean test(final Number item) {
                      final boolean isNotValid = predicate.negate().test(item);
              
                      if (isNotValid) {
                          log.warn("Invalid {}. Cause: {}", item, notSatisfiedLogMessage);
                      }
              
                      return predicate.test(item);
                  }
              }
              

              并使用:

              Predicate<Number> p = IS_NOT_NULL.and(IS_NOT_AN_INTEGER).and(IS_POSITIVE);
              

              【讨论】:

                【解决方案13】:

                在 jar 文件中创建常量时,让用户扩展枚举值通常很有帮助。我们使用枚举作为 PropertyFile 键并卡住了,因为没有人可以添加任何新的!下面会更好。

                给定:

                public interface Color {
                  String fetchName();
                }
                

                和:

                public class MarkTest {
                
                  public static void main(String[] args) {
                    MarkTest.showColor(Colors.BLUE);
                    MarkTest.showColor(MyColors.BROWN);
                  }
                
                  private static void showColor(Color c) {
                    System.out.println(c.fetchName());
                  }
                }
                

                罐子里可以有一个枚举:

                public enum Colors implements Color {
                  BLUE, RED, GREEN;
                  @Override
                  public String fetchName() {
                    return this.name();
                  }
                }
                

                用户可以扩展它来添加自己的颜色:

                public enum MyColors implements Color {
                  BROWN, GREEN, YELLOW;
                  @Override
                  public String fetchName() {
                    return this.name();
                  }
                }
                

                【讨论】:

                  【解决方案14】:

                  枚举就像 Java 类,它们可以有构造函数、方法等。唯一不能用它们做的事情是 new EnumName()。这些实例是在您的枚举声明中预定义的。

                  【讨论】:

                  • enum Foo extends SomeOtherClass 怎么样?所以和普通班不完全一样,事实上,完全不同。
                  • @Adam:我会说枚举与类相似的方式比它们与类不同的方式要多得多。但是,不,它们并不相同。
                  • 如果你反编译一个枚举,你会看到它是一个扩展枚举的类,并且在构造函数/初始化字段中已经实例化了“常量”。
                  【解决方案15】:

                  这就是我为什么...

                  我已经用 Enum 的值填充了 JavaFX ComboBox。我有一个 Identifiable 接口(指定一种方法:identify),它允许我指定任何对象如何向我的应用程序标识自己以进行搜索。这个接口使我能够扫描任何类型对象的列表(对象可用于标识的任何字段)以进行标识匹配。

                  我想在我的 ComboBox 列表中找到一个身份值的匹配项。为了在包含 Enum 值的 ComboBox 上使用此功能,我必须能够在我的 Enum 中实现 Identifiable 接口(碰巧,在 Enum 的情况下实现它是微不足道的)。

                  【讨论】:

                    【解决方案16】:

                    我在接口中使用了一个内部枚举来描述一种策略,以从那里保持实例控制(每个策略都是一个单例)。

                    public interface VectorizeStrategy {
                    
                        /**
                         * Keep instance control from here.
                         * 
                         * Concrete classes constructors should be package private.
                         */
                        enum ConcreteStrategy implements VectorizeStrategy {
                            DEFAULT (new VectorizeImpl());
                    
                            private final VectorizeStrategy INSTANCE;
                    
                            ConcreteStrategy(VectorizeStrategy concreteStrategy) {
                                INSTANCE = concreteStrategy;
                            }
                    
                            @Override
                            public VectorImageGridIntersections processImage(MarvinImage img) {
                                return INSTANCE.processImage(img);
                            }
                        }
                    
                        /**
                         * Should perform edge Detection in order to have lines, that can be vectorized.
                         * 
                         * @param img An Image suitable for edge detection.
                         * 
                         * @return the VectorImageGridIntersections representing img's vectors 
                         * intersections with the grids.
                         */
                        VectorImageGridIntersections processImage(MarvinImage img);
                    }
                    

                    枚举实现策略这一事实很方便允许枚举类充当其封闭实例的代理。它也实现了接口。

                    它是一种 strategyEnumProxy :P clent 代码如下所示:

                    VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);
                    

                    如果它没有实现它本来的接口:

                    VectorizeStrategy.ConcreteStrategy.DEFAULT.getInstance().processImage(img);
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2020-11-28
                      • 2011-05-05
                      • 2010-09-29
                      相关资源
                      最近更新 更多