Enums

Ist ein Enum eine Klasse?

Bei „enum“ handelt es sich zunächst einmal um ein Schlüsselwort, das dazu dient, eine Typdefinition einzuleiten, so wie „class“ und „interface“. Aber ja, eine Enum ist eine spezielle Art von Klasse. Man kann - mit bestimmten Einschränkungen - eine Enum so deklarieren und implementieren, wie man eine Klasse deklariert, insbesondere muss eine solche Enum-Klasse auch keinesfalls eine innere Klasse einer anderen Klasse sein, auch wenn das in Beispielen oft so ist. Und letztlich baut der Compiler aus einer Enum eine normale Klasse mit einer ganz normalen Class-Datei. Aber es gibt auch eine Klasse Enum<E>, die die gemeinsame direkte Superklasse aller Aufzählungstypen bildet.

Kann ich über eine Enum iterieren, z.B. mit der ForEach-Form der For-Schleife?

Nein, nicht direkt. Aber jede Enum-Klasse verfügt implizit immer über die statische Methode values(). Diese liefert ein Array, welches alle „Werte“ des enum liefert. Und über dieses Array kann man dann ganz normal iterieren, auch mit der ForEach-Schleife.

Wo finde ich die Methoden values() und valueOf(String name) in der API-Doku?

Leider überhaupt nicht. Es handelt sich um implizit deklarierte statische Methoden, deren Vorhandensein für jede Enum-Klasse man schlicht kennen und akzeptieren muss… einer der unschönen Sonderfälle der Sprache Java.

Problem mit enum in Aufgabe 3 der Nachklausur zum Sommersemester 2007

Frage:    In der Aufgabe geht es um eine Aufzählung mittels enum. Dabei kann man einerseits einer Variablen einen Wochentag.Freitag zuweisen, indem man die entsprechende Enum-Konstante verwendet. Später kann man dann aber genau diesen Freitag verändern, z.B. durch Aufruf der Methode naechsterTag(). Das hat dann den Effekt, daß der Wochentag zwar irgendwie Wochentag.Freitag ist, aber gleichzeitig wt.aktuellerTag Samstag liefert:

    Wochentag wt = Wochentag.Freitag;
    System.out.println("Wochentag: " + wt);
    System.out.println("aktueller Wochentag: " + wt.aktuellerTag);
    wt.naechsterTag();
    System.out.println("Wochentag: " + wt);
    System.out.println("aktueller Wochentag: " + wt.aktuellerTag);

Irgendwie scheint mir das irreführend zu sein. Wie und wo würde man denn diese seltsame Aufzählung sinnvoll einsetzen?

Antwort:    Da muss ich dir recht geben. Die Aufgabe ist so leider nicht wirklich sinnvoll und du hast auch schon genau erkannt, warum nicht.

Die neue Klasse Wochentag soll ja ein eine Enum-Klasse sein, welche die Wochentage repräsentiert. D.h. ein Exemplar dieser Klasse steht genau für einen Wochentag und kann selbst als Wert einer entsprechenden Variablen aktuellerTag verwendet werden. Die Wochentags-Enum dann noch einmal als einen „Behälter“ für einen Wert, der einen bestimmten Tag repräsentiert, zu gestalten, ergibt damit keinen Sinn.

Man kann aber das Beispiel so umbauen, dass es etwa dem entspricht, was in der Aufgabe eigentlich beabsichtigt war und dabei eine Enum sinnvoll verwendet. Dazu baue ich eine Klasse Tag, die die Rolle des Behälters für einen konkreten Wochentag bekommt, und in die ich die Funktionalität zur Bestimmung des vorigen/nächsten Tags einbaue. Das könnte etwa so aussehen:

public class Tag {

    public enum Wochentag {
        Montag, Dienstag, Mittwoch, Donnerstag,
        Freitag, Samstag, Sonntag
    }

    private Wochentag aktTag = Wochentag.Montag;

    public Wochentag getTag() {
        return aktTag;
    }

    public void setTag(Wochentag tag) {
        aktTag = tag;
    }

    public void naechsterTag() {
        aktTag = Wochentag.values()[(aktTag.ordinal() + 1) % 7];
    }

    public void vorhergehenderTag() {
        aktTag = Wochentag.values()[(aktTag.ordinal() + 6) % 7];
    }

    public static void main(String[] args) {
        Tag t = new Tag();
        t.setTag(Wochentag.Dienstag);
        t.naechsterTag();
        System.out.println(t.getTag());
    }
}

Schöner ist es natürlich, die Methoden zur Berechnung des nächsten/vorigen Tags gleich in die Enum-Klasse einbauen. Das sollte dann aber so geschehen, dass die Methoden wiederum ein Exemplar der Enum-Klasse liefern. Analog zu obigem Beispiel könnte das so aussehen:

public class Tag {

    private Wochentag aktTag = Wochentag.Montag;

    public Wochentag getTag() {
        return aktTag;
    }

    public void setTag(Wochentag tag) {
        aktTag = tag;
    }

    public static void main(String[] args) {
        Tag t = new Tag();
        t.setTag(Wochentag.Dienstag);
        t.setTag(t.getTag().naechsterTag());
        System.out.println(t.getTag());
    }
}

public enum Wochentag {
    Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag, Sonntag;

    public Wochentag naechsterTag() {
        return values()[(ordinal() + 1) % 7];
    }

    public Wochentag vorhergehenderTag() {
        return values()[(ordinal() + 6) % 7];
    }
}

In vielen Beispielen werden Enums innerhalb einer anderen Klasse definiert. Muss das so sein?

Nein, ganz im Gegenteil! In der Praxis wird man eine Enum so gut wie nie als innere Klasse modellieren wollen. Denn Enum-Typen sind normalerweise sehr "zentrale" Typen eines Programms: Man findet sie oft als Parameter oder Rückgabetypen von Methoden, über die verschiedene funktionale Untereinheiten eines Programms miteinander kommunizieren. Es wäre also ziemlich unsinnig, sie innerhalb einer anderen Klasse zu definieren. Dass man dies in Code-Beispielen trotzdem des öfteren findet, liegt nur daran, dass der jeweilige Autor sein Beispiel möglichst kompakt halten wollte.