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


当前回答

对我来说,使用枚举接口的最佳用例之一是谓词过滤器。这是一种非常优雅的方法来弥补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);
    }
}

其他回答

我在一个接口中使用了一个内部枚举来描述一个策略来保持实例控制(每个策略都是一个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);
}

枚举实现该策略的事实便于允许枚举类充当其所包含的Instance的代理。其中也实现了接口。

它是一种strategyEnumProxy:P,客户端代码看起来像这样:

VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);

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

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

这里一些人给出的可比示例是错误的,因为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
}

枚举不只是表示被动的集合(例如颜色)。它们可以表示具有功能的更复杂的对象,因此你可能想要在这些对象上添加更多的功能——例如,你可能有诸如Printable、Reportable等接口,以及支持这些接口的组件。

另一个可能性:

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

上面提到的策略并没有充分强调使用枚举的策略模式的轻量级实现:

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