为什么Enum会实现接口?

时间:2010-04-25 19:32:00

标签: java enums

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

16 个答案:

答案 0 :(得分:243)

这是一个例子(在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 :-/
}

现在获取Simple + Complex Operators的列表:

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

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

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

答案 1 :(得分:116)

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

答案 2 :(得分:19)

这里有几个人给出的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
}

答案 3 :(得分:12)

我经常使用一个案例。我有一个带有静态方法的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);

答案 4 :(得分:11)

由于Enums可以实现接口,因此可以使用它们来严格执行单例模式。试图使标准类成为单例允许......

  • 有可能使用反射技术将私有方法公开为公共
  • 用于继承你的单身人士并用其他东西覆盖你的单身人士的方法

Enums as monletons有助于防止这些安全问题。这可能是让Enums充当类和实现接口的原因之一。只是一个猜测。

有关详情,请参阅https://stackoverflow.com/questions/427902/java-enum-singletonSingleton class in java

答案 5 :(得分:9)

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

编辑:@Helper Method就是一个很好的例子。考虑让其他库定义新的运算符,然后告诉管理器类'嘿,这个枚举存在 - 注册它'。否则,您只能在自己的代码中定义操作符 - 没有可扩展性。

答案 6 :(得分:5)

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

我无法想到枚举不应该能够实现接口的原因,同时我也无法想到它们的合理理由。

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

答案 7 :(得分:5)

例如,如果您有Logger枚举。然后你应该在界面中使用logger方法,如debug,info,warning和error。它使你的代码松散耦合。

答案 8 :(得分:5)

最常见的用法是将两个枚举的值合并为一个组并对其进行类似处理。例如,了解如何join Fruits and Vegatables

答案 9 :(得分:3)

对我来说,使用enum与接口的最佳用例之一是Predicate过滤器。这是解决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);
    }
}

答案 10 :(得分:2)

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

答案 11 :(得分:2)

在jar文件中创建常量时,让用户扩展枚举值通常会很有帮助。下面是一个简单的示例,但是我们对属性文件键使用枚举,但由于没有人可以添加任何新键而被卡住!下面的效果会更好。

给出:

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

答案 12 :(得分:1)

这就是为什么......

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

我想在我的ComboBox列表中找到一个标识值的匹配项。为了在包含Enum值的ComboBox上使用此功能,我必须能够在我的Enum中实现Identifiable接口(在Enum的情况下,实现这一点很简单)。

答案 13 :(得分:1)

另一个可能性:

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

答案 14 :(得分:1)

上面提到策略的帖子还没有充分说明使用枚举对策略模式进行很好的轻量级实现会带来什么:

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读起来几乎就像是一种不错的松散类型语言!

答案 15 :(得分:0)

我在界面中使用了一个内部枚举,用于描述保持实例控制的策略(每个策略都是一个Singleton)。

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

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

这是一种策略EnumProxy:P这个简洁的代码如下所示:

VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);

如果它没有实现接口,那就是:

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