Відповіді:
Енуми не просто повинні представляти пасивні набори (наприклад, кольори). Вони можуть представляти більш складні об'єкти з функціональністю, і тому ви, ймовірно, захочете додати до них додаткову функціональність - наприклад, у вас можуть бути такі інтерфейси, як Printable
і Reportable
т.д., та компоненти, які їх підтримують.
Ось один приклад (подібний / кращий можна знайти в «Ефективній 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 +:
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 та клас Singleton в java для більш детального обговорення.
for inheriting from your singleton and overriding your singleton's methods with something else
. ви можете просто використовувати a, final class
щоб запобігти цьому
Це потрібно для розширення - якщо хтось використовує розроблений вами API, перерахунки, які ви визначаєте, статичні; вони не можуть бути додані або змінені. Однак якщо ви дозволите йому реалізувати інтерфейс, людина, що використовує API, може розробити власний перелік, використовуючи той самий інтерфейс. Потім ви можете зареєструвати цей enum у менеджера enum, який конгломерує enums разом зі стандартним інтерфейсом.
Редагувати: @ Метод Гельпера є прекрасним прикладом цього. Подумайте про те, щоб інші бібліотеки визначали нових операторів, а потім скажіть класу менеджера, що "ей, ця перерахування існує - зареєструйте її". В іншому випадку ви зможете визначити Операторів лише у власному коді - розширення не буде.
Енуми - це просто заняття у масках, тож здебільшого все, що ти можеш зробити з класом, ти можеш зробити із перерахунком.
Я не можу придумати причину того, що перерахунок не повинен бути в змозі реалізувати інтерфейс, і в той же час я не можу придумати їх вагомих причин.
Я б сказав, як тільки ви почнете додавати до перерахунку таку річ, як інтерфейси чи метод, ви дійсно повинні розглянути можливість створення цього класу. Звичайно, я впевнений, що є вагомі випадки, коли ви робите нетрадиційні речі, і тому, що обмеження буде штучним, я виступаю за те, щоб дозволити людям робити те, що вони хочуть.
Найпоширенішим використанням для цього було б об'єднання значень двох переліків в одну групу і трактування їх аналогічно. Наприклад, подивіться, як приєднатися до фруктів та овочів .
Одним з найкращих випадків використання для enum's з інтерфейсом є фільтри предикатів. Це дуже елегантний спосіб усунути відсутність типовості колекцій 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();
}
}
Ось моя причина, чому ...
Я заповнив JavaFX ComboBox зі значеннями Enum. У мене є інтерфейс, який можна ідентифікувати (із зазначенням одного методу: ідентифікувати), який дозволяє мені вказати, як будь-який об’єкт ідентифікує себе до моєї програми для пошукових цілей. Цей інтерфейс дозволяє мені сканувати списки будь-якого типу об'єктів (яке поле об'єкт може використовувати для ідентичності) для відповідності ідентичності.
Я хотів би знайти збіг за значенням ідентичності у своєму списку ComboBox. Для того, щоб використовувати цю можливість на моєму ComboBox, що містить значення Enum, я повинен мати можливість реалізовувати інтерфейс, що визначається в моєму Enum (що, як це буває, тривіально реалізувати у випадку Enum).
Я використовував внутрішній перелік в інтерфейсі, що описує стратегію, щоб звідти контролювати екземпляр (кожна стратегія є 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);
}
Той факт, що enum реалізує стратегію, зручно дозволити класу enum виконувати роль проксі для свого вкладеного екземпляра. який також реалізує інтерфейс.
це така собі стратегіяEnumProxy: P код коду виглядає так:
VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);
Якщо він не реалізував інтерфейс, це було б:
VectorizeStrategy.ConcreteStrategy.DEFAULT.getInstance().processImage(img);