Answers:
这是一个示例(在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()));
因此,在这里您使用接口来模拟可扩展的枚举(如果不使用接口就无法实现)。
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
}
我经常使用一种情况。我有一个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);
由于Enums可以实现接口,因此可以将它们用于严格执行单例模式。尝试使标准课程成为单身人士可以...
枚举作为单例有助于防止这些安全问题。这可能是让Enums充当类并实现接口的重要原因之一。只是一个猜测。
有关更多讨论,请参见/programming/427902/java-enum-singleton和Java中的Singleton类。
for inheriting from your singleton and overriding your singleton's methods with something else
。您可以使用a final class
来防止这种情况
枚举只是变相的类,因此在大多数情况下,可以对类进行的任何操作都可以对枚举进行处理。
我想不出枚举不应该实现接口的原因,同时我也想不出它们有任何理由。
我要说的是,一旦您开始向枚举添加接口或方法之类的东西,您就应该真正考虑使其成为一个类。当然,我确信存在做非传统枚举的合理方法,并且由于限制是人为限制,我赞成让人们在其中做自己想做的事情。
最常见的用法是将两个枚举的值合并为一组,并进行类似处理。例如,查看如何加入Fruits和Vegatables。
对于我来说,使用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);
}
}
上面提到策略的文章并没有足够强调使用枚举对策略模式进行很好的轻量级实现会给您带来什么:
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读起来几乎像是一种不错的松散类型语言!
枚举就像Java类一样,它们可以具有构造函数,方法等。您不能对它们进行操作new EnumName()
。这些实例是在您的枚举声明中预定义的。
enum Foo extends SomeOtherClass
呢 因此,与普通班级不太一样,实际上是完全不同的。
另一个可能性:
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);
在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();
}
}
我在接口中使用了一个内部枚举,描述了从那里保留实例控件(每个策略是一个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);