Зачем Enum реализовывать интерфейс?

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


person unj2    schedule 25.04.2010    source источник
comment
dzone.com/articles/java-extensible-enum-with-interface   -  person Please_Dont_Bully_Me_SO_Lords    schedule 14.11.2018


Ответы (16)


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

person Brian Agnew    schedule 25.04.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()));

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

person helpermethod    schedule 25.04.2010
comment
Сложные операторы могут быть pow и т.п. - person Pimgd; 18.08.2016
comment
Впервые увидел синтаксис Enum (с фигурными скобками после членов), и я программировал на Java уже 6 лет. TIL. - person jmrah; 30.08.2019

Пример 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
}
person Jorn    schedule 26.04.2010
comment
Да, странно !! Enum уже реализует интерфейс Comparable и не позволяет переопределить. Я просмотрел исходный код Java для класса enum и не смог найти реализацию compareTo. Может ли кто-нибудь сказать мне, что сравнивается в методе compareTo ENUM? - person Akh; 21.08.2012
comment
@AKh он сравнивает порядковые номера, что означает, что естественный порядок - это порядок, в котором константы перечисления находятся в исходном файле. - person Jorn; 22.08.2012
comment
Перечислимые переменные являются окончательными, поэтому сравнение происходит с помощью ==, верно? - person djangofan; 27.02.2019
comment
@djangofan да. - person Jorn; 27.02.2019

Есть чехол, которым я часто пользуюсь. У меня есть 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);
person sinuhepop    schedule 08.05.2012
comment
Именно то, что мне нужно! - person Petronella; 14.07.2020

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

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

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

См. https://stackoverflow.com/questions/427902/java-enum-singleton и Singleton-класс в java для более подробного обсуждения.

person Tansir1    schedule 25.04.2010
comment
for inheriting from your singleton and overriding your singleton's methods with something else. вы можете просто использовать final class, чтобы предотвратить это - person Dylanthepiguy; 19.04.2017

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

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

person Chris Dennett    schedule 25.04.2010

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

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

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

person TofuBeer    schedule 25.04.2010
comment
... так что по большей части все, что вы можете сделать с классом, вы можете сделать с помощью перечисления, но я не могу сказать, что мое перечисление наследуется от абстрактного класса. Так что да, по большей части акцент на. - person Adam Parkin; 24.04.2013
comment
Это потому, что все перечисления расширяют java.lang.Enum, а классы Java могут расширяться только из одного класса. - person TofuBeer; 24.04.2013

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

person Johan    schedule 29.04.2010

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

person husayt    schedule 04.11.2013

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

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

person Robert Moskal    schedule 05.11.2018

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

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

person Cosmin Cosmin    schedule 09.06.2011
comment
А что насчет enum Foo extends SomeOtherClass? Так что это не совсем то же самое, что и обычный класс, на самом деле совсем другое. - person Adam Parkin; 24.04.2013
comment
@ Адам: Я бы сказал, что способы, которыми Enums похожи на классы, намного более многочисленны, чем способы, которыми они отличаются от классов. Но нет, они не идентичны. - person scottb; 29.04.2013
comment
если вы декомпилируете перечисление, вы увидите, что это класс, который расширяет Enumeration и имеет константы, уже созданные в полях конструктора / инициализации. - person Cosmin Cosmin; 30.04.2013

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

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);
person Jorge Aranda    schedule 13.02.2018

При создании констант в файле 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();
  }
}
person markthegrea    schedule 03.09.2019

Вот почему ...

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

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

person scottb    schedule 29.04.2013

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

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 чистый код выглядит так:

VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);

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

VectorizeStrategy.ConcreteStrategy.DEFAULT.getInstance().processImage(img);
person juanmf    schedule 15.11.2015