Pourquoi un Enum implémenterait-il une interface?

Réponses:

130

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

Brian Agnew
la source
259

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 + 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 l'aide d'une interface).

méthode d'aide
la source
3
Les opérateurs complexes pourraient être "pow" et similaires.
Pimgd
2
C'est la première fois que je vois cette syntaxe Enum (avec les accolades après les membres), et je programme avec Java depuis 6 ans. TIL.
jrahhali
23

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

Un meilleur exemple est d'avoir une interface qui définit, disons, un type de données. Vous pouvez avoir une énumération pour implémenter les types simples et avoir des classes normales pour implémenter les 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
}
Jorn
la source
2
Oui, bizarre !!. Enum implémente déjà l'interface Comparable et ne permet pas de remplacer. J'ai recherché le code source java pour la classe enum et je n'ai pas pu trouver l'implémentation compareTo. Quelqu'un peut-il me dire ce qui est comparé dans une méthode compareTo un ENUM?
AKh
2
@AKh il compare les ordinaux, ce qui signifie que l'ordre naturel est l'ordre dans lequel les constantes d'énumération sont dans le fichier source.
Jorn
Les variables enum sont finales et donc la comparaison se produit avec ==, non?
djangofan
@djangofan oui.
Jorn
17

Il y a un cas que j'utilise souvent. J'ai une IdUtilclasse avec des méthodes statiques pour travailler avec des objets implémentant une Identifiableinterface très simple :

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

    }

Ensuite, je peux l'obtenir par son de idcette façon:

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

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

  • la possibilité d'utiliser des techniques de réflexion pour exposer des méthodes privées comme publiques
  • pour hériter de votre singleton et remplacer les méthodes de votre singleton par autre chose

Les énumérations en tant que singletons aident à éviter ces problèmes de sécurité. Cela aurait pu être l'une des raisons pour lesquelles Enums pouvait agir comme des classes et implémenter des interfaces. Juste une supposition.

Voir /programming/427902/java-enum-singleton et les classes Singleton en java pour plus de discussion.

Tansir1
la source
1
for inheriting from your singleton and overriding your singleton's methods with something else. vous pouvez simplement utiliser un final classpour empêcher cela
Dylanthepiguy
10

Elle est requise 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 sa propre énumération en utilisant la même interface. Vous pouvez ensuite enregistrer cette énumération auprès d'un gestionnaire d'énumérations qui regroupe les énumérations avec l'interface standard.

Edit: La méthode @Helper en est l'exemple parfait. Pensez à avoir d'autres bibliothèques définissant de nouveaux opérateurs et disant ensuite à une classe de gestionnaire que «hé, cette énumération existe - enregistrez-la». Sinon, vous ne pourriez définir des opérateurs que dans votre propre code - il n'y aurait pas d'extensibilité.

Chris Dennett
la source
7

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

Je ne peux pas penser à une raison pour laquelle une énumération ne devrait pas être en mesure d'implémenter 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 quelque chose comme des interfaces ou une méthode à une énumération, vous devriez vraiment envisager d'en faire une classe à la place. Bien sûr, je suis sûr qu'il existe des cas valables pour faire des choses d'énumération non traditionnelles, et puisque la limite serait artificielle, je suis en faveur de laisser les gens faire ce qu'ils veulent là-bas.

TofuBeer
la source
4
"... donc pour l'essentiel tout ce que vous pouvez faire avec une classe, vous pouvez le faire avec une énumération", mais je ne peux pas dire que mon énumération hérite d'une classe abstraite. Alors oui, l'accent est mis sur "pour la plupart".
Adam Parkin
5
En effet, toutes les énumérations étendent java.lang.Enum et les classes Java ne peuvent s'étendre qu'à partir d'une seule classe.
TofuBeer
6

Par exemple, si vous avez une énumération Logger. Ensuite, vous devriez avoir les méthodes de l'enregistreur telles que le débogage, les informations, les avertissements et les erreurs dans l'interface. Cela rend votre code faiblement couplé.

Johan
la source
6

L'utilisation la plus courante pour cela serait de fusionner les valeurs de deux énumérations en un seul groupe et de les traiter de la même manière. Par exemple, voyez comment rejoindre Fruits and Vegatables .

husayt
la source
5

L'un des meilleurs cas d'utilisation pour moi d'utiliser les énumérations avec l'interface est les filtres de prédicat. C'est un moyen très élégant de remédier au manque de typicité 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);
    }
}
Anton Bessonov
la source
5

Le post ci-dessus qui a mentionné les stratégies n'a pas suffisamment insisté sur ce qu'une belle implémentation légère du modèle de stratégie utilisant des énumérations vous apporte:

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 en un seul endroit sans avoir besoin d'une unité de compilation distincte pour chacune. Vous obtenez une belle répartition dynamique juste avec:

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

Fait lire Java presque comme une belle langue lâche!

Robert Moskal
la source
3

Les énumérations sont comme des classes Java, 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 d'énumération.

Cosmin Cosmin
la source
Et alors enum Foo extends SomeOtherClass? Ce n'est donc pas tout à fait la même chose qu'une classe ordinaire, en fait, tout à fait différent.
Adam Parkin
@Adam: Je dirais que les façons dont les Enums ressemblent à des classes sont beaucoup plus nombreuses que leurs différences avec les classes. Mais non, ils ne sont pas identiques.
scottb
si vous décompilez une énumération, vous verrez que c'est une classe qui étend l'énumération et dont les "constantes" sont déjà instanciées dans les champs constructeur / initialisation.
Cosmin Cosmin
3

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);
Jorge Aranda
la source
3

Lors de la création de constantes dans un fichier jar, il est souvent utile de laisser les utilisateurs étendre les valeurs d'énumération. Nous avons utilisé des énumérations pour les clés PropertyFile et nous sommes bloqués car personne ne pouvait en ajouter de nouvelles! Ci-dessous aurait mieux fonctionné.

Donné:

public interface Color {
  String fetchName();
}

et:

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

on pourrait avoir un enum dans le bocal:

public enum Colors implements Color {
  BLUE, RED, GREEN;
  @Override
  public String fetchName() {
    return this.name();
  }
}

et un utilisateur pourrait l'étendre pour ajouter ses propres couleurs:

public enum MyColors implements Color {
  BROWN, GREEN, YELLOW;
  @Override
  public String fetchName() {
    return this.name();
  }
}
markthegrea
la source
2

Voici ma raison pourquoi ...

J'ai rempli un ComboBox JavaFX avec les valeurs d'un Enum. J'ai une interface, identifiable (en spécifiant une méthode: identifier), qui me permet de spécifier comment tout objet s'identifie à 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 peut utiliser pour l'identité) pour une correspondance d'identité.

Je voudrais trouver une correspondance pour une valeur d'identité dans ma liste ComboBox. Afin d'utiliser cette capacité sur mon ComboBox contenant les valeurs Enum, je dois être en mesure d'implémenter l'interface identifiable dans mon Enum (ce qui, en l'occurrence, est trivial à implémenter dans le cas d'un Enum).

scottb
la source
1

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'énumération 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égie EnumProxy: P le code clent ressemble à ceci:

VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);

S'il n'avait pas implémenté l'interface, cela aurait été:

VectorizeStrategy.ConcreteStrategy.DEFAULT.getInstance().processImage(img);
juanmf
la source