06.05 Enumerations

Mit Enumerations (Enums) wurde in Java 1.5 ein lang vermisstes Element der Programmiersprache nachgereicht, welches in anderen Sprachen schon lang gang und gäbe ist. Durch Enums ist es möglich auf einfachste und sichere Art und Weise Aufzählungen zu realisieren, die früher über statische Konstante implementiert werden mussten.

Der alte Weg

Bis jetzt haben Sie immer Aufzählungen in Form von statischen, unveränderbaren und öffentlichen Klassenattributen realisiert (siehe Kapitel 04.07 Weitere Modifizierer).

public class AClass {

  public static final int AN_OPTION = 1;
  public static final int ANOTHER_OPTION = 2;
  public static final int DIFFERENT_OPTION = 3;

  public static final int IM_COMPLETLY_DIFFERENT = 4;

  public void doSomethingWithAnOption(int option) {
    ...
  }
}

Die Methode doSomethingWithAnOption soll mit einem der Klassenattribute AN_OPTION, ANOTHER_OPTION oder DIFFERENT_OPTION aufgerufen werden. Dies hat aber mehrere Nachteile:

  • Anstelle einer Konstanten aus dieser Klasse kann der Methode auch irgend ein Integer übergeben werden => doSomethingWithAnOption(5);.
  • Es besteht die Möglichkeit, dass versehentlich eine falsche Konstante übergeben wird => doSomethingWithAnOption(AClass.IM_COMPLETLY_DIFFERENT);.
  • Es wird nicht unbedingt klar, welche Konstanten verwendet werden sollen, und woher diese kommen.

Diese Probleme wurden mit der Einführung von Enumerations abgeschafft.

Enumerations

Mit dem Schlüsselwort enum können Aufzählungen eingeleitet werden. Dies sieht dann z. B. so aus:

enum Wochentag {MONTAG, DIENSTAG, MITTWOCH, DONNERSTAG, FREITAG, SAMSTAG, SONNTAG};

Die Schreibweise in Großbuchstaben resultiert daraus, dass es sich um Konstanten handelt, und diese in Java laut Code Conventions groß geschrieben werden. Siehe auch das Kapitel über Code Conventions

Es wird eine Aufzählung der Wochentage angelegt. Auf einen Wochentag können Sie ganz gewöhnlich über den Punkt zugreifen und ihn einer Variablen vom Typ Wochentag zuweisen.

Wochentag wtag = Wochentag.SAMSTAG;

Mit Ihren bisherigen Kenntnissen sollte es nun ein Leichtes sein, eine entsprechende Klasse mit dieser Aufzählung und einer Methode, die einen Wochentag erwartet, anzulegen.

package de.jbb;

public class EnumTest {

  public static enum Wochentag {MONTAG, DIENSTAG, MITTWOCH, DONNERSTAG, FREITAG, SAMSTAG, SONNTAG};
  
  public void wortZumTag(Wochentag tag) {
    
    if (tag == Wochentag.MONTAG) {
      System.out.println("Heute fängt die Woche an.");
    }
    else if (tag == Wochentag.DIENSTAG) {
      System.out.println("Für die meisten Deutschen der schlimmste Tag der Woche.");
    }
    else if (tag == Wochentag.MITTWOCH) {
      System.out.println("Die Hälfte ist schong geschafft.");
    }
    else if (tag == Wochentag.DONNERSTAG) {
      System.out.println("Durchhalten! Bald geht's ins Wochenende!");
    }
    else if (tag == Wochentag.FREITAG) {
      System.out.println("WOCHENENDE!!!");
    }
    else if (tag == Wochentag.SAMSTAG) {
      System.out.println("Endlich ausschlafen ...");
    }
    else if (tag == Wochentag.SONNTAG) {
      System.out.println("Noch ein bisschen entspannen, bevor die neue Woche anfängt.");
    }
  }
}

Zum Testen Ihrer Klasse können Sie sich eine neue Klasse mitsamt Main-Methode anlegen:

package de.test;

import de.jbb.EnumTest;

public class Test {
  
  public static void main(String args[]) throws Exception {
    
    EnumTest et = new EnumTest();
    et.wortZumTag(EnumTest.Wochentag.MITTWOCH);
  }
}

Eine Besonderheit von Enumerations ist, dass sie auch in einem switch-case-Block verwendet werden dürfen.

switch (tag) {
  case SAMSTAG:
  case SONNTAG:
    System.out.println("Wochenende");
    break;
  default:
    System.out.println("Unter der Woche");
}

Compiler Output

Sehen Sie sich nun einmal an, was der Compiler aus Ihrer EnumTest-Klasse gemacht hat. Es existiert neben der EnumTest.class-Datei auch eine EnumTest$Wochentag.class. Der Compiler hat aus der Enumeration also eine eigene Klasse erstellt. Diese wird so wie die inneren Klassen behandelt. Aber wenn der Compiler aus unserem Enum eine Klasse macht, dann muss es doch auch möglich sein die Enumeration Wochentag in eine separate Datei auszulagern!? Völlig korrekt!

package de.jbb;

public enum Wochentag {
  MONTAG, DIENSTAG, MITTWOCH, DONNERSTAG, FREITAG, SAMSTAG, SONNTAG
}

Sie fragen sich jetzt sicherlich, warum man so eine einfache Auflistung in eine separate Datei auslagern sollte. Die Antwort darauf wird Sie vielleicht überraschen: Weil eine Enumeration viel mehr als eine einfache Auflistung ist!

Enumerations erweitert

Sehen Sie sich zuerst einmal folgende Auflistung von Ländern an, die ich Ihnen dann im Anschluss erklären werde.

package de.jbb;

public enum Land {

  DEUTSCHLAND ("Berlin", 82169000, 357104.07),
  USA ("Washington D. C.", 304482526, 9826630),
  RUSSLAND ("Moskau", 142400000, 17075400),
  FRANKREICH ("Paris", 64473140, 672352);
  
  private int einwohner;
  private final double flaeche;
  private final String hauptstadt;
  
  private Land(String hauptstadt, int einwohner, double flaeche) {
    
    this.hauptstadt = hauptstadt;
    this.einwohner = einwohner;
    this.flaeche = flaeche;
  }

  public int getEinwohnerProQKM() {
    return (int)(this.einwohner / this.flaeche);
  }
  
  public void setEinwohner(int einwohner) { this.einwohner = einwohner; }
  
  public int getEinwohner() { return this.einwohner; }
  public double getFlaeche() { return this.flaeche; }
  public String getHauptstadt() { return this.hauptstadt; }
}

Wenn Sie das Anlegen der einzelnen Länder (

DEUTSCHLAND ("Berlin", 82169000, 357104.07),
USA ("Washington D. C.", 304482526, 9826630),
RUSSLAND ("Moskau", 142400000, 17075400),
FRANKREICH ("Paris", 64473140, 672352);

) vorerst nicht beachten, sieht diese Enumeration wie eine ganz gewöhnliche Klasse aus. Sie liegt in einem Package, hat einen Namen, Attribute und Methoden mit unterschiedlichen Modifizierern und einen Konstruktor. Sehen wir uns zuerst die Attribute an:

  • private int einwohner => Speichert die Einwohner eines Landes.
  • private final double flaeche => Speichert die Größe des Landes in Quadratkilometern. Ist final, da sich die Größe eines Landes so gut wie nie ändert.
  • private final String hauptstadt => Speichert die Hauptstadt des Landes. Ist final, da sich die Hauptstadt eines Lands nur sehr selten ändert.

Diese Attribute können über die entsprechende Getter-Methode abgefragt werden. Für die Einwohnerzahl, welche sich öfter ändern kann, besteht die Möglichkeit sie zusätzlich über eine Setter-Methode zu manipulieren. Die Methode getEinwohnerProQKM berechnet die Einwohnerzahl pro Quadratkilometer und gibt diese zurück.

Der Konstruktor ist private, da die Enumeration genau aus diesen Ländern besteht, und es von außen nicht möglich sein soll weitere Ländern hinzuzufügen.

Die eigentlichen Länder werden mit

DEUTSCHLAND ("Berlin", 82169000, 357104.07),
USA ("Washington D. C.", 304482526, 9826630),
RUSSLAND ("Moskau", 142400000, 17075400),
FRANKREICH ("Paris", 64473140, 672352);

erzeugt. Dabei wird der Name des Landes definiert, gefolgt vom Aufruf eines Konstruktors. Es wird also u. a. das Land „DEUTSCHLAND“ mit der Hauptstadt „Berlin“, 82.169.000 Einwohnern und einer Fläche von 357.104,07 km² generiert.

Auf die Eigenschaften dieser Länder können Sie wie bei jeder anderen Klasse auch zugreifen.

Land l = Land.DEUTSCHLAND;
System.out.println(l +": " + l.getEinwohner() + " Einwohner, " + 
  l.getFlaeche() + " Fläche, " + l.getEinwohnerProQKM());

Allgemeine Methoden

Enumerations haben auch allgemeine Methoden, die Sie nicht selbst definieren müssen.

  • values()
    Liefert ein Array mit allen Punkten dieser Enumeration zurück.
  • valueOf(String name)
    Liefert das Element der Enumeration zurück, die den übergebenen Namen trägt.
  • valueOf(Class c, String name)
    Liefert das Element der Enumeration zurück, welches den übergebenen Namen trägt und vom Typ der übergebenen Klasse ist (siehe Kapitel 18.XX Die Klasse Class).

Auch haben die einzelnen Elemente einer Enumeration ohne Ihr Zutun einige Methoden:

  • compareTo(Element e)
    Vergleicht zwei Elemente einer Enumeration. Das dazugehörige Interface Comparable wird in Kapitel D) Objekte sortieren – Comparator und Comparable besprochen.
  • getDeclaringClass()
    Gibt die Klasse des Elements zurück (siehe Kapitel 18.XX Die Klasse Class).
  • name()
    Gibt den Namen des Elements zurück
  • ordinal()
    Gibt einen Wert, der diesem Element bei der Initialisierung eindeutig zugeordnet wurde, als Ordinalzahl zurück

Außerdem enthält jedes Element einer Enumeration alle Methoden von Object, und überschreibt equals, hashCode und toString sinnvoll (siehe Kapitel 04.03.11 Besondere Methoden (equals, hashCode und toString)). Diese Methoden können in der Enumeration durch Sie natürlich trotzdem anderweitig überschrieben werden.

Enumerations Intern

Aus Enumerations werden in Java gewöhnliche Klassen kompiliert, die von der Klasse Enum<E extends Enum<E>> erben. Daher kommen auch die angesprochenen Standardmethoden. Sie wissen, dass Generics beim Kompilieren entfernt werden. Durch diese Maßnahme wird unsere Enumeration beim Kompilieren zu folgender Klasse umgewandelt:

public final class Land extends Enum {

  private Land(String s, int i, String hauptstadt, int einwohner, double flaeche) {
  	
    super(s, i);
    this.hauptstadt = hauptstadt;
    this.einwohner = einwohner;
    this.flaeche = flaeche;
  }

  public int getEinwohnerProQKM() {
    return (int)((double)einwohner / flaeche);
  }

  public void setEinwohner(int einwohner) {
    this.einwohner = einwohner;
  }

  public int getEinwohner() {
    return einwohner;
  }

  public double getFlaeche() {
    return flaeche;
  }

  public String getHauptstadt() {
    return hauptstadt;
  }

  public static Land[] values() {
    Land aland[];
    int i;
    Land aland1[];
    System.arraycopy(aland = ENUM$VALUES, 0, aland1 = new Land[i = aland.length], 0, i);
    return aland1;
  }

  public static Land valueOf(String s) {
    return (Land)Enum.valueOf(de.jbb.Land, s);
  }

  public static final Land DEUTSCHLAND;
  public static final Land USA;
  public static final Land RUSSLAND;
  public static final Land FRANKREICH;
  private int einwohner;
  private final double flaeche;
  private final String hauptstadt;
  private static final Land ENUM$VALUES[];

  static {
    DEUTSCHLAND = new Land("DEUTSCHLAND", 0, "Berlin", 0x4e5cca8, 357104.07D);
    USA = new Land("USA", 1, "Washington D. C.", 0x122608de, 9826630D);
    RUSSLAND = new Land("RUSSLAND", 2, "Moskau", 0x87cda00, 17075400D);
    FRANKREICH = new Land("FRANKREICH", 3, "Paris", 0x3d7c834, 672352D);
    ENUM$VALUES = (new Land[] {DEUTSCHLAND, USA, RUSSLAND, FRANKREICH});
  }
}

Unsere selbst definierten Methoden und Attribute sind soweit klar und benötigen keine weitere Erklärung.

Im Konstruktor wurden zwei weitere Parameter eingeschoben. Diese repräsentieren den Namen und die Ordinalzahl des Elements, welche durch Aufruf des Konstruktors der Klasse Enum gesetzt werden.

Als nächste Änderung fällt auf, dass die statischen Methoden valueOf(String s) und values() implementiert wurden. Der Inhalt dieser Methoden sollte für Sie ohne Probleme nachvollziehbar sein.

Kommen wir zum Interessanten Teil: Der Implementierung unserer Elemente. Wie Sie weiter unten im Code sehen können, werden diese als Klassenattribute in Form von Objekten der eigenen Klasse deklariert, und im statischen Initialisierer initialisiert. Die eigentlichen Elemente der Enumeration sind also nichts anderes als Objekte der Enumeration-Klasse. Die Reihenfolge (durch die Ordinalzahl bestimmt) entspricht der, wie Sie in unserem enum angelegt wurde.

Die letzte Konstante (ENUM$VALUES) – ein Array – speichert alle Elemente dieser Enumeration.

5 Replies to “06.05 Enumerations”

  1. Patrick

    Kann ich eine Enumeration auch mit zwei Arrays erstellen?

    So wie hier Deutschland erstellt wurde: DEUTSCHLAND („Berlin“, 82169000, 357104.07)

    Würde ich gerne so erstellen: DEUTSCHLAND (/*Array1*/, /*Array2*/)

  2. Patrick

    Already found it out by myself:

    DEUTSCHLAND(new String[] {„Angela“}, new String[] {„Merkel“})

Schreibe einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.