Почему Enum должен реализовывать интерфейс? - PullRequest
171 голосов
/ 25 апреля 2010

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

Ответы [ 15 ]

245 голосов
/ 26 апреля 2010

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

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

119 голосов
/ 25 апреля 2010

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

19 голосов
/ 26 апреля 2010

Пример Comparable, приведенный здесь несколькими людьми, неверен, поскольку Enum уже реализует это. Вы даже не можете переопределить это.

Лучшим примером является интерфейс, который определяет, скажем, тип данных. Вы можете иметь 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
}
12 голосов
/ 08 мая 2012

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

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

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

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

См. /294442/kakov-nailuchshii-podhod-dlya-ispolzovaniya-enum-v-kachestve-singltona-v-java и Синглтон-класс в Java для дальнейшего обсуждения.

9 голосов
/ 25 апреля 2010

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

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

5 голосов
/ 05 ноября 2013

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

5 голосов
/ 29 апреля 2010

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

5 голосов
/ 25 апреля 2010

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

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

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

3 голосов
/ 20 мая 2012

Один из лучших вариантов использования enum с интерфейсом - это фильтры предикатов. Это очень элегантный способ исправить отсутствие типичности коллекций 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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...