Почему Enum реализует интерфейс?

Я только узнал, что Java позволяет перечислениям реализовывать интерфейс. Что было бы хорошим вариантом для этого?

Ответ 1

Enums не просто должны представлять пассивные наборы (например, цвета). Они могут представлять более сложные объекты с функциональностью, и поэтому вы, вероятно, захотите добавить к ним дополнительные функции - например, вы можете иметь такие интерфейсы, как Printable, Reportable и т.д. и компоненты, которые их поддерживают.

Ответ 2

Здесь один пример (аналогичный/лучший найден в 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()));

Итак, здесь вы используете интерфейс для имитации расширяемых перечислений (что было бы невозможно без использования интерфейса).

Ответ 3

Пример 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
}

Ответ 4

Есть случай, который я часто использую. У меня есть класс 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);

Ответ 5

Поскольку Enums может реализовывать интерфейсы, они могут использоваться для строгого соблюдения шаблона singleton. Попытка сделать стандартный класс синглтон позволяет...

  • для возможности использования методов отражения для раскрытия частных методов как общедоступных
  • для наследования от вашего синглтона и переопределения ваших методов singleton с чем-то еще

Перечисления, поскольку синглеты помогают предотвратить эти проблемы безопасности. Возможно, это была одна из причин, по которым Enums может действовать как классы и реализовывать интерфейсы. Просто предположим.

Подробнее см. fooobar.com/questions/4061/... и класс Singleton в java..

Ответ 6

Требуется для расширяемости - если кто-то использует разработанный вами API, перечисления, которые вы определяете, являются статическими; они не могут быть добавлены или изменены. Однако, если вы позволите ему реализовать интерфейс, человек, использующий API, может разработать собственное перечисление, используя тот же интерфейс. Затем вы можете зарегистрировать это перечисление с помощью менеджера перечислений, который объединяет перечисления вместе со стандартным интерфейсом.

Изменить: метод @Helper имеет прекрасный пример этого. Подумайте о том, что другие библиотеки определяют новые операторы, а затем сообщают классу менеджера, что "эй, это перечисление существует - зарегистрируйте его". В противном случае вы сможете определять операторы только в своем собственном коде - не было бы расширяемости.

Ответ 7

Перечисления - это только маскировка классов, поэтому, по большей части, все, что вы можете сделать, с классом, который вы можете сделать с перечислением.

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

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

Ответ 8

Например, если у вас есть перечисление Logger. Затем вы должны иметь методы регистрации, такие как debug, info, warning и error в интерфейсе. Это делает ваш код слабо связанным.

Ответ 9

Наиболее частое использование для этого было бы объединить значения двух перечислений в одну группу и относиться к ним аналогично. Например, посмотрите, как присоединиться к Fruits and Vegatables.

Ответ 10

Один из лучших вариантов использования для 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);
    }
}

Ответ 11

Перечисления подобны Java-классам, они могут иметь конструкторы, методы и т.д. Единственное, что вы не можете с ними сделать, это new EnumName(). Экземпляры предопределены в вашей декларации перечисления.

Ответ 12

Вот моя причина, почему...

Я заполнил JavaFX ComboBox со значениями Enum. У меня есть интерфейс, идентифицируемый (с указанием одного метода: идентификация), который позволяет мне указать, как любой объект идентифицирует себя в моем приложении для поиска. Этот интерфейс позволяет мне сканировать списки объектов любого типа (в зависимости от того, какое поле может использовать объект для идентификации) для соответствия идентичности.

Я хотел бы найти соответствие для значения идентификатора в моем списке ComboBox. Чтобы использовать эту возможность в моем ComboBox, содержащем значения Enum, я должен иметь возможность реализовать интерфейс Identifiable в моем Enum (который, как это бывает, тривиально реализовать в случае Enum).

Ответ 13

Еще одна возможность:

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

Ответ 14

Пост выше, в котором упоминались стратегии, недостаточно затронул только то, что дает вам хорошая легкая реализация шаблона стратегии с использованием enums:

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 почти как хороший свободно напечатанный язык!

Ответ 15

Вы можете сделать гораздо больше с enum s. Вы можете добавить к ним методы и аргументы конструктора. Вы можете применить Comparable к вашему перечислению, чтобы сравнить их. Кроме того, enum являются классами, поэтому реализация интерфейса должна быть разрешена.

enum Planet implements Comparable<Planet> {
  EARTH(1),
  MARS(3); // ... and so on

  public final float distance;
  Planet(float distance) {
    this.distance = distance;
  }
  // compare distances
  public int compareTo(Planet other) {
    return this.distance - other.distance;
  }
  // sorry, I didnt check my code
}

Ответ 16

Я использовал внутреннее перечисление в интерфейсе, описывающем стратегию, чтобы сохранить контроль над экземпляром (каждая стратегия - Синглтон).

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 vectors 
     * intersections with the grids.
     */
    VectorImageGridIntersections processImage(MarvinImage img);
}

Тот факт, что перечисление реализует стратегию, удобно разрешить классу enum действовать как прокси-сервер для его закрытого экземпляра. который также реализует интерфейс.

это своего рода стратегияEnumProxy: P код clent выглядит следующим образом:

VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);

Если он не реализовал интерфейс, он был бы:

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

Ответ 17

При создании констант в 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();
  }
}