Перечисления не должны просто представлять пассивные наборы (например, цвета). Они могут представлять более сложные объекты с функциональностью, поэтому вы, вероятно, захотите добавить к ним дополнительные функции - например, у вас могут быть интерфейсы, такие как Printableи Reportableт. Д., И компоненты, которые их поддерживают.
Вот один пример (похожий / лучший находится в Effective Java 2nd Edition):
publicinterfaceOperator{
intapply(int a, int b);
}
publicenumSimpleOperatorsimplementsOperator{
PLUS {
intapply(int a, int b){ return a + b; }
},
MINUS {
intapply(int a, int b){ return a - b; }
};
}
publicenumComplexOperatorsimplementsOperator{
// 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()));
Итак, здесь вы используете интерфейс для имитации расширяемых перечислений (что было бы невозможно без использования интерфейса).
Впервые увидел синтаксис Enum (с фигурными скобками после членов), и я программировал на Java уже 6 лет. TIL.
jrahhali
23
ComparableПример , приведенный несколько людей здесь не так, так какEnum как уже реализует это. Вы даже не можете отменить это.
Лучшим примером является интерфейс, который определяет, скажем, тип данных. У вас может быть перечисление для реализации простых типов и обычные классы для реализации сложных типов:
interfaceDataType{
// methods here
}
enumSimpleDataTypeimplementsDataType{
INTEGER, STRING;
// implement methods
}
classIdentifierDataTypeimplementsDataType{
// implement interface and maybe add more specific methods
}
Да, странно !! Enum уже реализует интерфейс Comparable и не позволяет переопределить. Я просмотрел исходный код java для класса enum и не смог найти реализацию compareTo. Может ли кто-нибудь сказать мне, что сравнивается внутри метода compareTo ENUM?
Akh
2
@AKh он сравнивает порядковые номера, то есть естественный порядок - это порядок, в котором константы перечисления находятся в исходном файле.
Йорн
Перечислимые переменные являются окончательными, поэтому сравнение происходит с помощью ==, верно?
djangofan
@djangofan да.
Йорн
18
Есть чехол, которым я часто пользуюсь. У меня есть IdUtilкласс со статическими методами для работы с объектами, реализующими очень простой Identifiableинтерфейс:
publicinterfaceIdentifiable<K> {
K getId();
}
publicabstractclassIdUtil{
publicstatic <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;
}
}
returnnull;
}
publicstatic <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;
}
}
Поскольку Enums могут реализовывать интерфейсы, их можно использовать для строгого соблюдения одноэлементного шаблона. Попытка сделать стандартный класс singleton позволяет ...
для возможности использования методов отражения для предоставления частных методов как общедоступных
для наследования от вашего синглтона и переопределения методов вашего синглтона чем-то еще
Перечисления как синглтоны помогают предотвратить эти проблемы с безопасностью. Это могло быть одной из причин, по которым Enums могли действовать как классы и реализовывать интерфейсы. Просто догадка.
for inheriting from your singleton and overriding your singleton's methods with something else. вы можете просто использовать a, final classчтобы предотвратить это
Dylanthepiguy
10
Это требуется для расширяемости - если кто-то использует разработанный вами API, перечисления, которые вы определяете, являются статическими; они не могут быть добавлены или изменены. Однако, если вы позволите ему реализовать интерфейс, человек, использующий API, сможет разработать собственное перечисление, используя тот же интерфейс. Затем вы можете зарегистрировать это перечисление с помощью диспетчера перечислений, который объединяет перечисления вместе со стандартным интерфейсом.
Изменить: @Helper Method имеет прекрасный пример этого. Подумайте о том, чтобы другие библиотеки определяли новые операторы, а затем сообщали классу менеджера, что «эй, это перечисление существует - зарегистрируйте его». В противном случае вы могли бы определять операторы только в своем собственном коде - расширяемости не было бы.
Перечисления - это просто замаскированные классы, поэтому по большей части все, что вы можете делать с классом, вы можете делать с помощью перечисления.
Я не могу придумать причину, по которой перечисление не может реализовывать интерфейс, в то же время я не могу придумать для них веской причины.
Я бы сказал, что как только вы начнете добавлять в перечисление такие вещи, как интерфейсы или метод, вам действительно стоит подумать о том, чтобы вместо этого сделать его классом. Конечно, я уверен, что есть допустимые случаи для выполнения нетрадиционных вещей enum, и, поскольку ограничение будет искусственным, я за то, чтобы люди могли делать там то, что они хотят.
«... так что по большей части все, что вы можете сделать с классом, вы можете сделать с помощью перечисления», но я не могу сказать, что мое перечисление наследуется от абстрактного класса. Так что да, акцент на «по большей части».
Адам Паркин
5
Это связано с тем, что все перечисления расширяют java.lang.Enum, а классы Java могут расширяться только от одного класса.
TofuBeer
6
Например, если у вас есть перечисление Logger. Тогда у вас должны быть методы регистратора, такие как отладка, информация, предупреждение и ошибка в интерфейсе. Это делает ваш код слабосвязанным.
Чаще всего для этого используется объединение значений двух перечислений в одну группу и обращение с ними одинаково. Например, узнайте , как присоединиться к Fruits и Vegatables .
В сообщении выше, в котором упомянутые стратегии, недостаточно подчеркивается, что дает вам хорошая легкая реализация шаблона стратегии с использованием перечислений:
publicenumStrategy{
A {
@Overridevoidexecute(){
System.out.print("Executing strategy A");
}
},
B {
@Overridevoidexecute(){
System.out.print("Executing strategy B");
}
};
abstractvoidexecute();
}
Вы можете собрать все свои стратегии в одном месте, не требуя для каждой отдельной единицы компиляции. Вы получаете красивую динамическую отправку с помощью:
Strategy.valueOf("A").execute();
Делает java читаемым почти как приятный слабо типизированный язык!
Один из лучших вариантов использования enum с интерфейсом - фильтры Predicate. Это очень элегантный способ исправить недостаточную типичность коллекций apache (если другие библиотеки нельзя использовать).
Перечисления похожи на классы Java, они могут иметь конструкторы, методы и т. Д. Единственное, что вы не можете с ними делать - это new EnumName(). Экземпляры предопределены в вашем объявлении перечисления.
О чем enum Foo extends SomeOtherClass? Так что это не совсем то же самое, что и обычный класс, на самом деле совсем другое.
Адам Паркин
@ Адам: Я бы сказал, что способы, которыми Enums похожи на классы, намного более многочисленны, чем способы, которыми они отличаются от классов. Но нет, они не идентичны.
Скотт
если вы декомпилируете перечисление, вы увидите, что это класс, расширяющий Enumeration и имеющий «константы», уже созданные в полях конструктора / инициализации.
Cosmin Cosmin
3
Еще одна возможность:
publicenumConditionsToBeSatisfiedimplementsPredicate<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");
privatefinal Predicate<Number> predicate;
privatefinal String notSatisfiedLogMessage;
ConditionsToBeSatisfied(final Predicate<Number> predicate, final String notSatisfiedLogMessage) {
this.predicate = predicate;
this.notSatisfiedLogMessage = notSatisfiedLogMessage;
}
@Overridepublicbooleantest(final Number item){
finalboolean 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 и застряли, потому что никто не мог добавить новые! Ниже сработало бы намного лучше.
Я заполнил JavaFX ComboBox значениями Enum. У меня есть интерфейс Identifiable (с указанием одного метода: identify), который позволяет мне указать, как любой объект идентифицирует себя для моего приложения в целях поиска. Этот интерфейс позволяет мне сканировать списки объектов любого типа (какое бы поле объект ни использовал для идентификации) на предмет совпадения.
Я хотел бы найти соответствие значению идентичности в моем списке ComboBox. Чтобы использовать эту возможность в моем ComboBox, содержащем значения Enum, я должен иметь возможность реализовать интерфейс Identifiable в моем Enum (который, как оказалось, тривиально реализовать в случае Enum).
Я использовал внутреннее перечисление в интерфейсе, описывающем стратегию сохранения управления экземпляром (каждая стратегия - это синглтон) оттуда.
publicinterfaceVectorizeStrategy{
/**
* Keep instance control from here.
*
* Concrete classes constructors should be package private.
*/enumConcreteStrategyimplementsVectorizeStrategy{
DEFAULT (new VectorizeImpl());
privatefinal VectorizeStrategy INSTANCE;
ConcreteStrategy(VectorizeStrategy concreteStrategy) {
INSTANCE = concreteStrategy;
}
@Overridepublic 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 чистый код выглядит так:
Ответы:
Перечисления не должны просто представлять пассивные наборы (например, цвета). Они могут представлять более сложные объекты с функциональностью, поэтому вы, вероятно, захотите добавить к ним дополнительные функции - например, у вас могут быть интерфейсы, такие как
Printable
иReportable
т. Д., И компоненты, которые их поддерживают.источник
Вот один пример (похожий / лучший находится в 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 могут реализовывать интерфейсы, их можно использовать для строгого соблюдения одноэлементного шаблона. Попытка сделать стандартный класс singleton позволяет ...
Перечисления как синглтоны помогают предотвратить эти проблемы с безопасностью. Это могло быть одной из причин, по которым Enums могли действовать как классы и реализовывать интерфейсы. Просто догадка.
См. Https://stackoverflow.com/questions/427902/java-enum-singleton и класс Singleton в java для более подробного обсуждения.
источник
for inheriting from your singleton and overriding your singleton's methods with something else
. вы можете просто использовать a,final class
чтобы предотвратить этоЭто требуется для расширяемости - если кто-то использует разработанный вами API, перечисления, которые вы определяете, являются статическими; они не могут быть добавлены или изменены. Однако, если вы позволите ему реализовать интерфейс, человек, использующий API, сможет разработать собственное перечисление, используя тот же интерфейс. Затем вы можете зарегистрировать это перечисление с помощью диспетчера перечислений, который объединяет перечисления вместе со стандартным интерфейсом.
Изменить: @Helper Method имеет прекрасный пример этого. Подумайте о том, чтобы другие библиотеки определяли новые операторы, а затем сообщали классу менеджера, что «эй, это перечисление существует - зарегистрируйте его». В противном случае вы могли бы определять операторы только в своем собственном коде - расширяемости не было бы.
источник
Перечисления - это просто замаскированные классы, поэтому по большей части все, что вы можете делать с классом, вы можете делать с помощью перечисления.
Я не могу придумать причину, по которой перечисление не может реализовывать интерфейс, в то же время я не могу придумать для них веской причины.
Я бы сказал, что как только вы начнете добавлять в перечисление такие вещи, как интерфейсы или метод, вам действительно стоит подумать о том, чтобы вместо этого сделать его классом. Конечно, я уверен, что есть допустимые случаи для выполнения нетрадиционных вещей enum, и, поскольку ограничение будет искусственным, я за то, чтобы люди могли делать там то, что они хотят.
источник
Например, если у вас есть перечисление Logger. Тогда у вас должны быть методы регистратора, такие как отладка, информация, предупреждение и ошибка в интерфейсе. Это делает ваш код слабосвязанным.
источник
Чаще всего для этого используется объединение значений двух перечислений в одну группу и обращение с ними одинаково. Например, узнайте , как присоединиться к Fruits и Vegatables .
источник
В сообщении выше, в котором упомянутые стратегии, недостаточно подчеркивается, что дает вам хорошая легкая реализация шаблона стратегии с использованием перечислений:
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 читаемым почти как приятный слабо типизированный язык!
источник
Один из лучших вариантов использования 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); } }
источник
Перечисления похожи на классы 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); } }
и используя:
источник
При создании констант в файле 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(); } }
источник
Вот почему ...
Я заполнил JavaFX ComboBox значениями Enum. У меня есть интерфейс Identifiable (с указанием одного метода: identify), который позволяет мне указать, как любой объект идентифицирует себя для моего приложения в целях поиска. Этот интерфейс позволяет мне сканировать списки объектов любого типа (какое бы поле объект ни использовал для идентификации) на предмет совпадения.
Я хотел бы найти соответствие значению идентичности в моем списке ComboBox. Чтобы использовать эту возможность в моем ComboBox, содержащем значения Enum, я должен иметь возможность реализовать интерфейс Identifiable в моем Enum (который, как оказалось, тривиально реализовать в случае Enum).
источник
Я использовал внутреннее перечисление в интерфейсе, описывающем стратегию сохранения управления экземпляром (каждая стратегия - это синглтон) оттуда.
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 чистый код выглядит так:
Если бы он не реализовал интерфейс, это было бы:
источник