607 Stimmen

Können Enums untergeordnet werden, um neue Elemente hinzuzufügen?

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?

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.

17voto

Guillaume Husta Punkte 3501

Falls du es verpasst hast, gibt es ein Kapitel im ausgezeichneten Buch von Joshua Bloch "Effektive Java, 2. Auflage".

  • Kapitel 6 - Enums und Annotationen
  • Punkt 34 : Erstellen von erweiterbaren Enums mit Interfaces nachahmen

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.

13voto

Juan Pablo G Punkte 731

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.

7voto

Laurent Caillette Punkte 1141

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

}

7voto

Khaled Lela Punkte 6697

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

6voto

sulai Punkte 4918

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.com

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.

Powered by:

X