为什么枚举实现接口?


Answers:


130

枚举不仅仅代表被动集(例如颜色)。他们可以代表与功能更复杂的对象,所以你可能想进一步功能添加到这些是那么-例如,你可能如接口PrintableReportable等等。支持这些和组件。


259

这是一个示例(在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()));

因此,在这里您使用接口来模拟可扩展的枚举(如果不使用接口就无法实现)。


3
复杂的运算符可以是“ pow”等。
Pimgd

2
第一次见到Enum语法(成员后面有花括号),而我从事Java编程已有6年了。瓷砖。
jrahhali

23

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
}

2
是的,很奇怪!枚举已经实现了Comparable接口,并且不允许重写。我查找了enum类的Java源代码,无法找到compareTo实现。谁能告诉我在ENUM的compareTo方法中比较了什么?
AKh 2012年

2
@AKh会比较序数,这意味着自然顺序是枚举常量在源文件中的顺序。
乔恩(Jorn)2012年

枚举var是最终变量,因此与==进行比较,对吗?
djangofan

@djangofan是的。
乔恩

17

我经常使用一种情况。我有一个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);

13

由于Enums可以实现接口,因此可以将它们用于严格执行单例模式。尝试使标准课程成为单身人士可以...

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

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

有关更多讨论,请参见/programming/427902/java-enum-singletonJava中的Singleton类


1
for inheriting from your singleton and overriding your singleton's methods with something else。您可以使用a final class来防止这种情况
Dylanthepiguy

10

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

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


7

枚举只是变相的类,因此在大多数情况下,可以对类进行的任何操作都可以对枚举进行处理。

我想不出枚举不应该实现接口的原因,同时我也想不出它们有任何理由。

我要说的是,一旦您开始向枚举添加接口或方法之类的东西,您就应该真正考虑使其成为一个类。当然,我确信存在做非传统枚举的合理方法,并且由于限制是人为限制,我赞成让人们在其中做自己想做的事情。


4
“ ...因此,在大多数情况下,您可以使用类进行任何操作,也可以使用枚举进行处理”,但是我不能说让枚举继承自抽象类。是的,重点是“大部分”。
亚当·帕金

5
那是因为所有枚举都扩展了java.lang.Enum,而Java类只能从一个类扩展。
TofuBeer

6

例如,如果您有Logger枚举。然后,您应该在界面中使用诸如调试,信息,警告和错误之类的记录器方法。它使您的代码松散耦合。



5

对于我来说,使用enum with with接口的最佳用例之一是谓词过滤器。这是补救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);
    }
}

5

上面提到策略的文​​章并没有足够强调使用枚举对策略模式进行很好的轻量级实现会给您带来什么:

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


3

枚举就像Java类一样,它们可以具有构造函数,方法等。您不能对它们进行操作new EnumName()。这些实例是在您的枚举声明中预定义的。


enum Foo extends SomeOtherClass呢 因此,与普通班级不太一样,实际上是完全不同的。
亚当·帕金

@亚当:我要说的是,枚举类似于类的方式要比区别于类的方式多得多。但是,不,它们并不相同。
斯科特

如果您反编译一个枚举,则会看到它是一个扩展Enumeration的类,并且已经在构造函数/初始化字段中实例化了“常量”。
Cosmin Cosmin

3

另一个可能性:

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

3

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

2

这就是我为什么...

我用一个Enum的值填充了JavaFX ComboBox。我有一个可识别的接口(指定一种方法:identify),该接口使我可以指定任何对象如何针对我的应用程序进行自我识别以进行搜索。通过该界面,我可以扫描任何类型的对象(对象可以用于标识的任何字段)的列表以进行标识匹配。

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


1

我在接口中使用了一个内部枚举,描述了从那里保留实例控件(每个策略是一个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);
}

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

这是一种strategyEnumProxy:P的完整代码如下所示:

VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);

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

VectorizeStrategy.ConcreteStrategy.DEFAULT.getInstance().processImage(img);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.