Чому Enum реалізує інтерфейс?


Відповіді:


130

Енуми не просто повинні представляти пасивні набори (наприклад, кольори). Вони можуть представляти більш складні об'єкти з функціональністю, і тому ви, ймовірно, захочете додати до них додаткову функціональність - наприклад, у вас можуть бути такі інтерфейси, як Printableі Reportableт.д., та компоненти, які їх підтримують.


259

Ось один приклад (подібний / кращий можна знайти в «Ефективній 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()));

Отже, тут ви використовуєте інтерфейс для імітації розширюваних перерахунків (що було б неможливо без використання інтерфейсу).


3
Складні оператори можуть бути "порошком" тощо.
Пімгд

2
Вперше побачив синтаксис Enum (із фігурними дужками після членів), і я програмував на Java вже 6 років. TIL.
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
Так. Дивно !!. Enum вже реалізує порівнянний інтерфейс і не дозволяє переосмислювати. Я подивився вихідний код Java для класу enum і не зміг знайти порівняння для реалізації. Хто-небудь може сказати мені, що порівнюється всередині методу CompareTo ENUM?
Ах

2
@AKh він порівнює порядки, тобто природне впорядкування - це порядок, за яким константи перерахунків знаходяться у вихідному файлі.
Jorn

Enum vars є остаточним, тому порівняння відбувається з ==, правда?
djangofan

@djangofan так.
Jorn

18

Я часто використовую випадок. У мене є 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-singleton та клас Singleton в java для більш детального обговорення.


1
for inheriting from your singleton and overriding your singleton's methods with something else. ви можете просто використовувати a, final classщоб запобігти цьому
Dylanthepiguy

10

Це потрібно для розширення - якщо хтось використовує розроблений вами API, перерахунки, які ви визначаєте, статичні; вони не можуть бути додані або змінені. Однак якщо ви дозволите йому реалізувати інтерфейс, людина, що використовує API, може розробити власний перелік, використовуючи той самий інтерфейс. Потім ви можете зареєструвати цей enum у менеджера enum, який конгломерує enums разом зі стандартним інтерфейсом.

Редагувати: @ Метод Гельпера є прекрасним прикладом цього. Подумайте про те, щоб інші бібліотеки визначали нових операторів, а потім скажіть класу менеджера, що "ей, ця перерахування існує - зареєструйте її". В іншому випадку ви зможете визначити Операторів лише у власному коді - розширення не буде.


7

Енуми - це просто заняття у масках, тож здебільшого все, що ти можеш зробити з класом, ти можеш зробити із перерахунком.

Я не можу придумати причину того, що перерахунок не повинен бути в змозі реалізувати інтерфейс, і в той же час я не можу придумати їх вагомих причин.

Я б сказав, як тільки ви почнете додавати до перерахунку таку річ, як інтерфейси чи метод, ви дійсно повинні розглянути можливість створення цього класу. Звичайно, я впевнений, що є вагомі випадки, коли ви робите нетрадиційні речі, і тому, що обмеження буде штучним, я виступаю за те, щоб дозволити людям робити те, що вони хочуть.


4
"... тож здебільшого все, що ти можеш зробити з класом, який ти можеш зробити із перерахунком", але я не можу сказати, чи моє перерахування успадковується з абстрактного класу. Так що так, акцент на "здебільшого".
Адам Паркін

5
Це тому, що переліки всі розширюють java.lang.Enum, а Java-класи можуть поширюватися лише з одного класу.
TofuBeer

6

Наприклад, якщо у вас є перелік Logger. Тоді ви повинні мати такі методи реєстратора, як налагодження, інформація, попередження та помилка в інтерфейсі. Це робить ваш код вільно поєднаним.



5

Одним з найкращих випадків використання для 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);
    }
}

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? Тож не зовсім те саме, що звичайний клас, насправді зовсім інше.
Адам Паркін

@Adam: Я б сказав, що способи, якими Enums нагадують класи, набагато численніші, ніж способи, якими вони відрізняються від класів. Але, ні, вони не тотожні.
scottb

якщо декомпілювати enum, ви побачите, що це клас, який розширює перерахування та має "константи", які вже інстанціюються в полях конструктора / ініціалізації.
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

Ось моя причина, чому ...

Я заповнив JavaFX ComboBox зі значеннями Enum. У мене є інтерфейс, який можна ідентифікувати (із зазначенням одного методу: ідентифікувати), який дозволяє мені вказати, як будь-який об’єкт ідентифікує себе до моєї програми для пошукових цілей. Цей інтерфейс дозволяє мені сканувати списки будь-якого типу об'єктів (яке поле об'єкт може використовувати для ідентичності) для відповідності ідентичності.

Я хотів би знайти збіг за значенням ідентичності у своєму списку ComboBox. Для того, щоб використовувати цю можливість на моєму ComboBox, що містить значення Enum, я повинен мати можливість реалізовувати інтерфейс, що визначається в моєму Enum (що, як це буває, тривіально реалізувати у випадку 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);
}

Той факт, що enum реалізує стратегію, зручно дозволити класу enum виконувати роль проксі для свого вкладеного екземпляра. який також реалізує інтерфейс.

це така собі стратегіяEnumProxy: P код коду виглядає так:

VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);

Якщо він не реалізував інтерфейс, це було б:

VectorizeStrategy.ConcreteStrategy.DEFAULT.getInstance().processImage(img);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.