Réponses:
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
, Reportable
etc. et des composants qui les prennent en charge.
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).
L' Comparable
exemple donné par plusieurs personnes ici est faux, car il le 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 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
}
Il y a un cas que j'utilise souvent. J'ai une IdUtil
classe avec des méthodes statiques pour travailler avec des objets implémentant une Identifiable
interface 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 id
cette façon:
MyEnum e = IdUtil.get(MyEnum.class, 1);
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 ...
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.
for inheriting from your singleton and overriding your singleton's methods with something else
. vous pouvez simplement utiliser un final class
pour empêcher cela
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é.
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.
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 .
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);
}
}
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!
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.
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.
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);
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();
}
}
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).
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);