Ich möchte ein bereits vorhandenes enum nehmen und weitere Elemente wie folgt hinzufügen:
enum A {a,b,c}
enum B erweitert A {d}
/*B ist {a,b,c,d}*/
Ist dies in Java möglich?
Ich möchte ein bereits vorhandenes enum nehmen und weitere Elemente wie folgt hinzufügen:
enum A {a,b,c}
enum B erweitert A {d}
/*B ist {a,b,c,d}*/
Ist dies in Java möglich?
Falls du es verpasst hast, gibt es ein Kapitel im ausgezeichneten Buch von Joshua Bloch "Effektive Java, 2. Auflage".
Nur die Schlussfolgerung :
Ein kleiner Nachteil bei der Verwendung von Interfaces zur Nachahmung von Erweiterbaren Enums ist, dass Implementierungen nicht von einem Enum Typ zum anderen vererbt werden können. Im Fall unseres Operation-Beispiels wird die Logik zum Speichern und Abrufen des mit einer Operation verbundenen Symbols in BasicOperation und ExtendedOperation dupliziert. In diesem Fall ist es jedoch unerheblich, da nur sehr wenig Code dupliziert wird. Wenn es mehr gemeinsame Funktionalität gäbe, könnte man sie in einer Hilfsklasse oder einer statischen Hilfsmethode kapseln, um die Code-Duplizierung zu beseitigen.
Zusammenfassend lässt sich sagen, dass man zwar keinen erweiterbaren Enum-Typ erstellen kann, dies aber durch das Schreiben eines Interfaces für einen einfachen Enum-Typ, der das Interface implementiert, nachahmen kann. Dadurch können Nutzer ihre eigenen Enums schreiben, die das Interface implementieren. Diese Enums können dann überall dort verwendet werden, wo der einfache Enum-Typ verwendet werden kann, vorausgesetzt, dass die APIs in Bezug auf das Interface geschrieben sind.
Hier ist ein Weg, wie ich herausgefunden habe, wie man ein Enum in ein anderes Enum erweitern kann, ist ein sehr geradliniger Ansatz:
Angenommen, Sie haben ein Enum mit gemeinsamen Konstanten:
public interface ICommonInterface {
String getName();
}
public enum CommonEnum implements ICommonInterface {
P_EDITABLE("editable"),
P_ACTIVE("active"),
P_ID("id");
private final String name;
EnumCriteriaComun(String name) {
name= name;
}
@Override
public String getName() {
return this.name;
}
}
dann können Sie versuchen, auf diese Weise manuell zu erweitern:
public enum SubEnum implements ICommonInterface {
P_EDITABLE(CommonEnum.P_EDITABLE ),
P_ACTIVE(CommonEnum.P_ACTIVE),
P_ID(CommonEnum.P_ID),
P_NEW_CONSTANT("new_constant");
private final String name;
EnumCriteriaComun(CommonEnum commonEnum) {
name= commonEnum.name;
}
EnumCriteriaComun(String name) {
name= name;
}
@Override
public String getName() {
return this.name;
}
}
natürlich müssen Sie jedes Mal, wenn Sie eine Konstante erweitern müssen, Ihre SubEnum-Dateien anpassen.
Dies ist, wie ich das Aufzählungsmuster mit Vererbung mit Laufzeitprüfung im statischen Initialisierer verbessere. Der BaseKind#checkEnumExtender
überprüft, ob die "erweiternde" Aufzählung alle Werte der Basisklasse genau auf die gleiche Weise deklariert, sodass #name()
und #ordinal()
vollständig kompatibel bleiben.
Es ist immer noch eine Kopie-Einfügen erforderlich, um Werte zu deklarieren, aber das Programm schlägt fehl, wenn jemand einen Wert in der Basisklasse hinzugefügt oder geändert hat, ohne die erweiternden Werte zu aktualisieren.
Gemeinsames Verhalten für verschiedene Aufzählungen, die sich gegenseitig erweitern:
public interface Kind {
/**
* Sagen wir, wir möchten ein zusätzliches Element.
*/
String description();
/**
* Standard-{@code Enum}-Methode.
*/
String name();
/**
* Standard-{@code Enum}-Methode.
*/
int ordinal();
}
Basis-Aufzählung, mit Überprüfungsmethode:
public enum BaseKind implements Kind {
FIRST( "Erstes" ),
SECOND( "Zweites" ),
;
private final String description;
public String description() {
return description;
}
private BaseKind(final String description) {
this.description = description;
}
public static void checkEnumExtender(
final Kind[] baseValues,
final Kind[] extendingValues
) {
if (extendingValues.length < baseValues.length) {
throw new IncorrectExtensionError("Nur " + extendingValues.length + " Werte gegen "
+ baseValues.length + " Grundwerte");
}
for (int i = 0; i < baseValues.length; i++) {
final Kind baseValue = baseValues[i];
final Kind extendingValue = extendingValues[i];
if (baseValue.ordinal() != extendingValue.ordinal()) {
throw new IncorrectExtensionError("Basisordinal " + baseValue.ordinal()
+ " stimmt nicht mit " + extendingValue.ordinal() + " überein");
}
if (!baseValue.name().equals(extendingValue.name())) {
throw new IncorrectExtensionError("Basenamen[ " + i + "] " + baseValue.name()
+ " stimmt nicht mit " + extendingValue.name() + " überein");
}
if (!baseValue.description().equals(extendingValue.description())) {
throw new IncorrectExtensionError("Beschreibung[ " + i + "] " + baseValue.description()
+ " stimmt nicht mit " + extendingValue.description() + " überein");
}
}
}
public static class IncorrectExtensionError extends Error {
public IncorrectExtensionError(final String s) {
super(s);
}
}
}
Erweiterungsbeispiel:
public enum ExtendingKind implements Kind {
FIRST(BaseKind.FIRST),
SECOND(BaseKind.SECOND),
THIRD("Drittes"),
;
private final String description;
public String description() {
return description;
}
ExtendingKind(final BaseKind baseKind) {
this.description = baseKind.description();
}
ExtendingKind(final String description) {
this.description = description;
}
}
Basierend auf der Antwort von @Tom Hawtin - tackline fügen wir Unterstützung für den Switch hinzu,
interface Day {
...
T valueOf();
}
public enum Weekday implements Day {
MON, TUE, WED, THU, FRI;
Weekday valueOf(){
return valueOf(name());
}
}
public enum WeekendDay implements Day {
SAT, SUN;
WeekendDay valueOf(){
return valueOf(name());
}
}
Day wds = Weekday.MON;
Day wends = WeekendDay.SUN;
switch(wds.valueOf()){
case MON:
case TUE:
case WED:
case THU:
case FRI:
}
switch(wends.valueOf()){
case SAT:
case SUN:
}
Ich neige dazu, Enums zu vermeiden, weil sie nicht erweiterbar sind. Um beim Beispiel des OP zu bleiben, wenn A in einer Bibliothek und B in Ihrem eigenen Code ist, können Sie A nicht erweitern, wenn es ein Enum ist. So ersetze ich manchmal Enums:
// Zugriff wie bei einem Enum: A.a
public class A {
public static final A a = new A();
public static final A b = new A();
public static final A c = new A();
/*
* Falls Sie Ihre Konstante in verschiedenen JVMs identifizieren müssen,
* benötigen Sie eine ID. Dies ist der Fall, wenn
* Ihr Objekt zwischen verschiedenen JVM-Instanzen übertragen wird
* (z.B. speichern/laden oder Netzwerk).
* Außerdem funktionieren switch-Anweisungen nicht mit
* Objekten, sondern mit int.
*/
public static int maxId=0;
public int id = maxId++;
public int getId() { return id; }
}
public class B extends A {
/*
* gut: Sie können so machen
* A x = getYourEnumFromSomeWhere();
* if(x instanceof B) ...;
* um zu identifizieren, welchem Enum x
* angehört.
*/
public static final A d = new A();
}
public class C extends A {
/* Gut: e.getId() != d.getId()
* Schlecht: In verschiedenen JVMs könnten C und B
* in unterschiedlicher Reihenfolge initialisiert werden,
* was zu unterschiedlichen IDs führt.
* Workaround: Verwenden Sie einen festen int oder Hashcode.
*/
public static final A e = new A();
public int getId() { return -32489132; };
}
Es gibt einige Fallstricke zu vermeiden, siehe die Kommentare im Code. Abhängig von Ihren Bedürfnissen ist dies eine solide, erweiterbare Alternative zu Enums.
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
15 Stimmen
Ein Grund dafür ist, dies zu tun, ist das Testen der Situation, in der ein ungültiger Enum-Wert vorhanden ist, ohne einen ungültigen Enum-Wert im Kernquellcode einzuführen.
0 Stimmen
Ja, ein Beispiel für "linguistische" Reinheit. Ich denke, was gewünscht wird, ist die "buchhalterische" Arbeitserleichterungsidee eines automatisch inkrementierenden Satzes von Ganzzahlen wie man es in C++ hat, damit man einen neuen Satz als Erweiterung des alten Satzes beginnen kann, beginnend bei 1 + dem letzten Wert des vorherigen Satzes, und wenn die Einträge benannt sind, die Namen aus dem "gemeinsamen Subset" erben. Obwohl das Java Enum einige schöne Dinge an sich hat, fehlt ihm die einfache automatisierte Hilfe zur Deklaration von Auto-Inkrementierungsintegern, die C++ Enum bereitstellt.
5 Stimmen
Tatsächlich, wenn Sie Ihren Aufzählungstyp mit neuen Werten erweitern, erstellen Sie keine Unterklasse, sondern eine Oberklasse. Sie können überall die Basisenumwerte anstelle des „erweiterten“ Enums verwenden, jedoch nicht umgekehrt. Daher ist gemäß dem Liskovschen Substitutionsprinzip der erweiterte Aufzählungstyp die Oberklasse des Basisenums.
0 Stimmen
@Ilya ... ja, das ist wahr. Ich weise darauf hin, dass die Frage eindeutige praktische Anwendungsfälle hat. Um des Arguments willen, betrachten Sie einen Basisklasse-Enum von:
Primärfarben
; es ist vernünftig, dies zu einer Überklasse-EnumPrimär- und Pastellfarben
zu unterklasse durch Hinzufügen neuer Farbnamen. Liskov ist immer noch der Elefant im Raum. Also warum nicht mit einem Basisklasse-Enum von:AllMeineFarben
beginnen -- Und dann könnte man alle Farben zu:Primär- und Pastellfarben
unterklasse und anschließend diese zu:Primärfarben
unterklasse (unter Berücksichtigung der Hierarchie). Java erlaubt das jedoch auch nicht.