web-dev-qa-db-fra.com

Pourquoi un Enum implémenterait-il une interface?

Je viens de découvrir que Java permet aux enums d'implémenter une interface. Quel serait un bon cas d'utilisation pour cela?

173
unj2

Les enums ne doivent pas simplement représenter des ensembles passifs (par exemple, des couleurs). Ils peuvent représenter des objets plus complexes avec des fonctionnalités, et vous voudrez alors probablement ajouter d'autres fonctionnalités à ceux-ci - par exemple. vous pouvez avoir des interfaces telles que Printable, Reportable etc. et des composants qui les prennent en charge.

121
Brian Agnew

Voici un exemple (un similaire/meilleur se trouve dans 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 :-/
}

Maintenant, pour obtenir une liste des deux opérateurs simples et complexes:

List<Operator> operators = new ArrayList<Operator>();

operators.addAll(Arrays.asList(SimpleOperators.values()));
operators.addAll(Arrays.asList(ComplexOperators.values()));

Donc, ici, vous utilisez une interface pour simuler des énumérations extensibles (ce qui ne serait pas possible sans utiliser une interface).

250
helpermethod

L'exemple Comparable donné par plusieurs personnes ici est faux, car Enum le met déjà en œuvre. Vous ne pouvez même pas le remplacer.

Un meilleur exemple est d'avoir une interface qui définit, par exemple, un type de données. Vous pouvez avoir une énumération pour implémenter les types simples et des classes normales pour implémenter des types compliqués:

interface DataType {
  // methods here
}

enum SimpleDataType implements DataType {
  INTEGER, STRING;

  // implement methods
}

class IdentifierDataType implements DataType {
  // implement interface and maybe add more specific methods
}
19
Jorn

Il y a un cas que j'utilise souvent. J'ai une classe IdUtil avec des méthodes statiques pour travailler avec des objets implémentant une interface très simple 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;
    }
}

Si je crée un Identifiableenum:

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

    }

Ensuite, je peux l'obtenir par sa id de cette façon:

MyEnum e = IdUtil.get(MyEnum.class, 1);
13
sinuhepop

Comme Enums peut implémenter des interfaces, elles peuvent être utilisées pour appliquer strictement le modèle singleton. Essayer de faire une classe standard un singleton permet ...

  • pour la possibilité d'utiliser des techniques de réflexion pour exposer les méthodes privées en public
  • pour hériter de votre singleton et surcharger les méthodes de votre singleton avec autre chose

Les énumérations en tant que singlet permettent d'éviter ces problèmes de sécurité. C’est peut-être l’une des raisons qui ont poussé Enums à agir en tant que classes et à implémenter des interfaces. Juste une supposition.

Voir https://stackoverflow.com/questions/427902/Java-enum-singleton et classe Singleton en Java pour plus d'informations.

12
Tansir1

C'est nécessaire pour l'extensibilité - si quelqu'un utilise une API que vous avez développée, les énumérations que vous définissez sont statiques; ils ne peuvent pas être ajoutés ou modifiés. Cependant, si vous le laissez implémenter une interface, la personne utilisant l'API peut développer son propre enum en utilisant la même interface. Vous pouvez ensuite inscrire cette énumération auprès d’un gestionnaire d’énumérations qui les conglomérera avec l’interface standard.

Edit: @Helper Method en est l'exemple parfait. Pensez à ce que d'autres bibliothèques définissent de nouveaux opérateurs, puis dites à la classe de gestionnaires que "hé, cette énumération existe - enregistrez-la". Sinon, vous ne pourrez définir des opérateurs que dans votre propre code - il n'y aurait pas d'extensibilité.

9
Chris Dennett

L’usage le plus courant consiste à fusionner les valeurs de deux enums dans un groupe et à les traiter de la même manière. Par exemple, voyez comment rejoindre Fruits et légumes .

5
husayt

Les énumérations ne sont que des classes déguisées, donc, pour la plupart, tout ce que vous pouvez faire avec une classe que vous pouvez faire avec une énumération.

Je ne peux pas penser à une raison pour laquelle une enum ne devrait pas être capable de mettre en place une interface, en même temps je ne peux pas penser à une bonne raison pour eux non plus.

Je dirais qu'une fois que vous commencez à ajouter des choses comme des interfaces, ou une méthode à une énumération, vous devriez vraiment envisager d'en faire une classe. Bien sûr, je suis sûr qu'il existe des cas valables pour faire des choses énormes non traditionnelles, et comme la limite serait artificielle, je suis en faveur de laisser les gens faire ce qu'ils veulent là-bas.

5
TofuBeer

Par exemple, si vous avez un enum de journal. Ensuite, vous devriez avoir les méthodes de journalisation telles que débogage, informations, avertissement et erreur dans l'interface. Cela rend votre code faiblement couplé.

5
Johan

Un des meilleurs cas d’utilisation d’enum avec interface est les filtres Predicate. C'est un moyen très élégant de remédier au manque de typage des collections Apache (si d'autres bibliothèques ne peuvent pas être utilisées).

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);
    }
}
3
Anton Bessonov

Les énumérations sont comme Java Les classes, elles peuvent avoir des constructeurs, des méthodes, etc. La seule chose que vous ne pouvez pas faire avec elles est new EnumName(). Les instances sont prédéfinies dans votre déclaration enum.

2
Cosmin Cosmin

Le message ci-dessus mentionnant les stratégies mentionnées ne traitait pas suffisamment de ce qu'une mise en œuvre légère et agréable du modèle de stratégie à l'aide d'énums vous procure:

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

Vous pouvez avoir toutes vos stratégies au même endroit sans avoir besoin d'une unité de compilation distincte pour chacune. Vous obtenez une dépêche dynamique de Nice avec juste:

Strategy.valueOf("A").execute();

Fait que Java se lit presque comme un langage joliment typé!

1
Robert Moskal

Voici ma raison pour laquelle ...

J'ai rempli un ComboBox JavaFX avec les valeurs d'un Enum. J'ai une interface, Identifiable (spécifiant une méthode: identifier), qui me permet de spécifier comment un objet s'identifie dans mon application à des fins de recherche. Cette interface me permet d'analyser des listes de tout type d'objets (quel que soit le champ que l'objet puisse utiliser pour l'identité) afin de rechercher une correspondance d'identité.

J'aimerais trouver une correspondance pour une valeur d'identité dans ma liste ComboBox. Pour pouvoir utiliser cette fonctionnalité sur ma ComboBox contenant les valeurs Enum, je dois pouvoir implémenter l'interface identifiable dans mon Enum (qui est simple à implémenter dans le cas d'un Enum).

1
scottb

Une autre posibilité:

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

et en utilisant:

Predicate<Number> p = IS_NOT_NULL.and(IS_NOT_AN_INTEGER).and(IS_POSITIVE);
1
Jorge Aranda

J'ai utilisé une énumération interne dans une interface décrivant une stratégie pour garder le contrôle d'instance (chaque stratégie est un singleton) à partir de là.

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

Le fait que l'enum implémente la stratégie est pratique pour permettre à la classe enum d'agir en tant que proxy pour son instance incluse. qui implémente également l'interface.

c'est une sorte de stratégieEnumProxy: P le code client ressemble à ceci:

VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);

Si l'interface n'avait pas été implémentée, ce serait:

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