Antworten:
Aufzählungen müssen nicht nur passive Mengen (z. B. Farben) darstellen. Sie können komplexere Objekte mit Funktionalität darstellen und so sind Sie dann wahrscheinlich weitere Funktionalität zu diesem hinzufügen wollen - zB Sie Schnittstellen haben können wie Printable
, Reportable
usw. und Komponenten , die diese unterstützen.
Hier ist ein Beispiel (ein ähnliches / besseres findet sich in 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 :-/
}
So erhalten Sie eine Liste der beiden einfachen + komplexen Operatoren:
List<Operator> operators = new ArrayList<Operator>();
operators.addAll(Arrays.asList(SimpleOperators.values()));
operators.addAll(Arrays.asList(ComplexOperators.values()));
Hier verwenden Sie also eine Schnittstelle, um erweiterbare Aufzählungen zu simulieren (was ohne die Verwendung einer Schnittstelle nicht möglich wäre).
Das Comparable
Beispiel, das hier von mehreren Personen gegeben wurde, ist falsch, da dies Enum
bereits umgesetzt wird. Sie können es nicht einmal überschreiben.
Ein besseres Beispiel ist eine Schnittstelle, die beispielsweise einen Datentyp definiert. Sie können eine Aufzählung zum Implementieren der einfachen Typen und normale Klassen zum Implementieren komplizierter Typen haben:
interface DataType {
// methods here
}
enum SimpleDataType implements DataType {
INTEGER, STRING;
// implement methods
}
class IdentifierDataType implements DataType {
// implement interface and maybe add more specific methods
}
Es gibt einen Fall, den ich oft benutze. Ich habe eine IdUtil
Klasse mit statischen Methoden, um mit Objekten zu arbeiten, die eine sehr einfache Identifiable
Schnittstelle implementieren:
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;
}
}
Wenn ich ein 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;
}
}
Dann kann ich es id
so bekommen:
MyEnum e = IdUtil.get(MyEnum.class, 1);
Da Enums Schnittstellen implementieren können, können sie zur strikten Durchsetzung des Singleton-Musters verwendet werden. Der Versuch, eine Standardklasse zu einem Singleton zu machen, ermöglicht ...
Aufzählungen als Singletons helfen, diese Sicherheitsprobleme zu vermeiden. Dies könnte einer der Gründe dafür gewesen sein, dass Enums als Klassen fungieren und Schnittstellen implementieren. Nur eine Vermutung.
Weitere Informationen finden Sie unter /programming/427902/java-enum-singleton und der Singleton-Klasse in Java .
for inheriting from your singleton and overriding your singleton's methods with something else
. Sie können nur ein verwenden final class
, um das zu verhindern
Dies ist für die Erweiterbarkeit erforderlich. Wenn jemand eine von Ihnen entwickelte API verwendet, sind die von Ihnen definierten Aufzählungen statisch. Sie können nicht hinzugefügt oder geändert werden. Wenn Sie jedoch eine Schnittstelle implementieren lassen, kann die Person, die die API verwendet, mithilfe derselben Schnittstelle eine eigene Aufzählung erstellen. Sie können diese Aufzählung dann bei einem Aufzählungsmanager registrieren, der die Aufzählungen zusammen mit der Standardschnittstelle zusammenfasst.
Edit: @Helper Method hat das perfekte Beispiel dafür. Stellen Sie sich vor, andere Bibliotheken definieren neue Operatoren und teilen dann einer Manager-Klasse mit, dass "Hey, diese Aufzählung existiert - registrieren Sie sie". Andernfalls können Sie Operatoren nur in Ihrem eigenen Code definieren - es gibt keine Erweiterbarkeit.
Aufzählungen sind nur verkleidete Klassen, also zum größten Teil alles, was Sie mit einer Klasse tun können, können Sie mit einer Aufzählung tun.
Ich kann mir keinen Grund vorstellen, warum eine Aufzählung keine Schnittstelle implementieren kann, und gleichzeitig kann ich mir auch keinen guten Grund dafür vorstellen.
Ich würde sagen, sobald Sie anfangen, Dinge wie Schnittstellen oder Methoden zu einer Aufzählung hinzuzufügen, sollten Sie wirklich darüber nachdenken, sie stattdessen zu einer Klasse zu machen. Natürlich bin ich mir sicher, dass es gültige Fälle für nicht-traditionelle Aufzählungssachen gibt, und da die Grenze künstlich wäre, bin ich dafür, die Leute dort tun zu lassen, was sie wollen.
Am häufigsten wird dies verwendet, um die Werte von zwei Aufzählungen zu einer Gruppe zusammenzuführen und sie ähnlich zu behandeln. Sehen Sie sich beispielsweise an, wie Sie Obst und Gemüse verbinden .
Einer der besten Anwendungsfälle für mich, Enums mit Schnittstelle zu verwenden, sind Prädikatfilter. Dies ist eine sehr elegante Methode, um die mangelnde Typizität von Apache-Sammlungen zu beheben (falls andere Bibliotheken möglicherweise nicht verwendet werden).
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);
}
}
Der obige Beitrag, in dem Strategien erwähnt wurden, hat nicht genug betont, was eine schöne, leichte Implementierung des Strategiemusters unter Verwendung von Aufzählungen bringt:
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();
}
Sie können alle Ihre Strategien an einem Ort haben, ohne für jede eine separate Kompilierungseinheit zu benötigen. Sie erhalten einen schönen dynamischen Versand nur mit:
Strategy.valueOf("A").execute();
Lässt Java fast wie eine schöne, lose getippte Sprache lesen!
Aufzählungen sind wie Java-Klassen, sie können Konstruktoren, Methoden usw. haben. Das einzige, was Sie damit nicht tun können, ist new EnumName()
. Die Instanzen sind in Ihrer Enum-Deklaration vordefiniert.
enum Foo extends SomeOtherClass
? Also nicht ganz das Gleiche wie eine reguläre Klasse, in der Tat ganz anders.
Eine andere Möglichkeit:
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);
}
}
und mit:
Predicate<Number> p = IS_NOT_NULL.and(IS_NOT_AN_INTEGER).and(IS_POSITIVE);
Beim Erstellen von Konstanten in einer JAR-Datei ist es häufig hilfreich, Benutzer Enum-Werte erweitern zu lassen. Wir haben Enums für PropertyFile-Schlüssel verwendet und sind hängen geblieben, weil niemand neue hinzufügen konnte! Unten hätte viel besser funktioniert.
Gegeben:
public interface Color {
String fetchName();
}
und:
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());
}
}
man könnte eine Aufzählung im Glas haben:
public enum Colors implements Color {
BLUE, RED, GREEN;
@Override
public String fetchName() {
return this.name();
}
}
und ein Benutzer könnte es erweitern, um seine eigenen Farben hinzuzufügen:
public enum MyColors implements Color {
BROWN, GREEN, YELLOW;
@Override
public String fetchName() {
return this.name();
}
}
Hier ist mein Grund warum ...
Ich habe eine JavaFX ComboBox mit den Werten einer Aufzählung gefüllt. Ich habe eine identifizierbare Schnittstelle (Angabe einer Methode: Identifizieren), mit der ich angeben kann, wie sich ein Objekt für Suchzwecke in meiner Anwendung identifiziert. Über diese Schnittstelle kann ich Listen aller Arten von Objekten (unabhängig davon, welches Feld das Objekt für die Identität verwendet) nach einer Identitätsübereinstimmung durchsuchen.
Ich möchte eine Übereinstimmung für einen Identitätswert in meiner ComboBox-Liste finden. Um diese Funktion in meiner ComboBox mit den Enum-Werten verwenden zu können, muss ich in der Lage sein, die identifizierbare Schnittstelle in meiner Enum zu implementieren (was im Fall einer Enum trivial zu implementieren ist).
Ich habe eine innere Aufzählung in einer Schnittstelle verwendet, die eine Strategie beschreibt, um die Instanzkontrolle (jede Strategie ist ein Singleton) von dort aus zu behalten.
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);
}
Die Tatsache, dass die Aufzählung die Strategie implementiert, ist praktisch, damit die Aufzählungsklasse als Proxy für ihre eingeschlossene Instanz fungieren kann. das implementiert auch die Schnittstelle.
Es ist eine Art StrategieEnumProxy: P Der Clent-Code sieht folgendermaßen aus:
VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);
Wenn es die Schnittstelle nicht implementiert hätte, wäre es gewesen:
VectorizeStrategy.ConcreteStrategy.DEFAULT.getInstance().processImage(img);