Pourquoi un Enum implémenterait-il une interface?

Je viens de découvrir que Java permet aux énumérations d’implémenter une interface. Quel serait un bon usage pour cela?

Les énumérations ne doivent pas seulement représenter des ensembles passifs (par exemple des couleurs). Ils peuvent représenter des objects plus complexes avec des fonctionnalités, et vous souhaiterez probablement y append des fonctionnalités supplémentaires. Par exemple, vous pouvez avoir des interfaces telles que Printable , Reportable etc. et des composants Reportable charge ces fonctionnalités.

Voici un exemple (un modèle 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 operators = new ArrayList(); 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).

L’exemple Comparable donné par plusieurs personnes est erroné, puisque Enum met 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 un enum pour implémenter les types simples et avoir 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 } 

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

  • pour la possibilité d’utiliser des techniques de reflection pour exposer les méthodes privées en tant que public
  • pour hériter de votre singleton et remplacer les méthodes de votre singleton par autre chose

Enums comme singletons aident à prévenir ces problèmes de sécurité. Cela pourrait être l’une des raisons consortingbuant à laisser Enums agir comme classes et implémenter des interfaces. Juste une supposition.

Voir https://stackoverflow.com/questions/427902/java-enum-singleton et Singleton class dans Java pour plus de discussion.

Il y a un cas que j’utilise souvent. J’ai une classe IdUtil avec des méthodes statiques pour travailler avec des objects qui implémentent une interface Identifiable très simple:

 public interface Identifiable { K getId(); } public abstract class IdUtil { public static  & Identifiable, S> T get(Class type, S id) { for (T t : type.getEnumConstants()) { if (Util.equals(t.getId(), id)) { return t; } } return null; } public static  & Identifiable, S extends Comparable> List getLower(T en) { List 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 une enum Identifiable :

  public enum MyEnum implements Identifiable { FIRST(1), SECOND(2); private int id; private MyEnum(int id) { this.id = id; } public Integer getId() { return id; } } 

Alors je peux l'obtenir par son id cette façon:

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

Il 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 enregistrer cette enum avec un gestionnaire de énumération qui conglomère les énumérations avec l’interface standard.

Edit: La méthode @Helper en est l’exemple parfait. Pensez à ce que d’autres bibliothèques définissent de nouveaux opérateurs et disent ensuite à une classe de responsable que «cette énumération existe – enregistrez-la». Sinon, vous ne pourrez définir des opérateurs que dans votre propre code – il n’y aura pas d’extensibilité.

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

Les enums ne sont que des classes déguisées, donc, pour la plupart, tout ce que vous pouvez faire avec un cours que vous pouvez faire avec un enum.

Je ne peux pas penser à une raison pour laquelle une énumération ne devrait pas pouvoir 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 à append une 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 certain qu’il y a des cas valables pour faire des choses non traditionnelles, et comme la limite serait artificielle, je suis en faveur de laisser les gens faire ce qu’ils veulent.

L’utilisation la plus courante 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 joindre Fruits et Vegatables .

L’un des meilleurs cas d’utilisation pour utiliser enum’s avec interface est celui des 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 Ssortingng 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 Ssortingng component = DEFAULT_COMPONENT; public static void main(Ssortingng[] args) { Collection tests = new ArrayList(); tests.add(new Test()); CollectionUtils.filter(tests, FilterTest.Active); } } 

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

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 object s’identifie à mon application à des fins de recherche. Cette interface me permet d’parsingr des listes de tous types d’objects (quel que soit le domaine dans lequel l’object peut servir d’identité) pour une correspondance d’identité.

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

Une autre possibilité:

 public enum ConditionsToBeSatisfied implements Predicate { 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 predicate; private final Ssortingng notSatisfiedLogMessage; ConditionsToBeSatisfied(final Predicate predicate, final Ssortingng 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 p = IS_NOT_NULL.and(IS_NOT_AN_INTEGER).and(IS_POSITIVE); 

J’ai utilisé un enum interne dans une interface décrivant une stratégie pour garder le contrôle de l’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 comme proxy pour son instance incluse. qui implémente également l’interface.

C’est une sorte de stratégieEnumProxy: P le code de clent ressemble à ceci:

 VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img); 

Si elle n’implémentait pas l’interface, cela aurait été:

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