1.11. Aufzählungstypen (enums)
Ein Aufzählungstyp (engl. enumeration type) kann als eine bestimmte Art
einer normalen Klasse angesehen werden und wird mit Hilfe des Schlüsselwortes
enum deklariert.
KlassenModifiers_opt enum EnumName {
...
} Der Körper des Aufzählungstyps kann Enum-Konstanten
enthalten. Für eine Enum-Konstante gilt: Eine Enum-Konstante definiert eine
Instanz eines Aufzählungstyps. Diese Aussage soll im Laufe des Abschnitts näher
erläutert werden. Zunächst soll der anschließende Java-Quelltext angegeben werden,
der den Aufzählungstyp EnumExample definiert. Zwischen den
geschweiften Klammern sind die vier Enum-Konstanten A , B ,
C und D notiert, die im Quelltext durch Kommata
voneinander getrennt sind.
Listing 1.44. EnumExample.java . Deklaration des Aufzählungstyps
EnumExample . Der Körper des Typs enthält die Enum-Konstanten
A , B , C und D .
public enum EnumExample {
A, B, C, D
}
Der Zugriff auf Enum-Konstanten kann mit Hilfe einer Punktnotation erfolgen.
Nach dem folgenden Muster kann der Variablen obj eine Instanz eines
Aufzählungstyps bezüglich einer Enum-Konstanten zugewiesen werden (siehe dazu
auch Abschnitt 1.11.2).
EnumName obj = EnumName.EnumKonstanteName Die Deklaration eines Aufzählungstyps kann auch in den Körper einer
normalen Klasse eingebettet werden. Das anschließende Beispiel zeigt diese
Möglichkeit. Darin wird der Aufzählungstyp Character deklariert.
Dieser wird direkt von der äußeren Klasse EnumExample2 umgeben.
Der Aufzählungstyp bzw. die deklarierten Enum-Konstanten werden innerhalb
einer switch -Anweisung verwendet, um den Buchstaben A bis D
entsprechende Vornamen zuzuordnen.
Listing 1.45. EnumExample2.java . Der Aufzählungstyp Character
wird innerhalb eines Klassenkörpers eingebettet.
public class EnumExample2 {
public enum Character {
A, B, C, D
}
public static String word(Character c) {
String str ="";
switch (c) {
case A: str = "Alida";
break;
case B: str = "Ben";
break;
case C: str = "Clara";
break;
case D: str = "Dennis";
break;
}
return str;
}
public static void main(String[] args) {
Character c1 = Character.A;
System.out.println(word(c1));
System.out.println(Character.D + ": " + word(Character.D));
}
}
Wie der Aufruf von javac EnumExample2.java zeigt, wird
für den Aufzählungstyp die separate Klassendatei
EnumExample2$Character.class generiert. Das Beispielprogramm gibt
"Alida" und "D: Dennis" auf dem Bildschirm aus. Dabei wird deutlich, dass
Character.D innerhalb der runden Klammern von println
zur Ausgabe der Namensbezeichnung der entsprechenden Enum-Konstanten führt.
1.11.2. Interne Realisierung von Aufzählungstypen
Ein Aufzählungstyp kann als "aufgesetzter Typ" gesehen werden, der einem
gewissen Konzept unterliegt. Der Bytecode (siehe dazu Abschnitt 4)
der mit javac erstellten Klassendatei EnumExample.class
kann untersucht werden, um die eigentliche Realisierung eines Aufzählungstypen
festzustellen.
Es kann ein geeignetes Programm (Disassembler, Decompiler) verwendet werden, um
z.B. die Anzahl der Felder und Methoden festzustellen, welche in der erzeugten
Klassendatei vorhanden sind. Durch die Bytecodeanalyse können die folgenden
Informationen gewonnen werden:
Klasse: public final enum EnumExample
Superklasse: java/lang/Enum
Feld: public static final enum EnumExample A
Feld: public static final enum EnumExample B
Feld: public static final enum EnumExample C
Feld: public static final enum EnumExample D
Feld: private static final synthetic EnumExample[] $VALUES
Konstruktor: private EnumExample(java/lang/String,int) {
super(java/lang/String,int);
}
Statischer Initialisierer: A = new EnumExample("A", 0)
B = new EnumExample("B", 1)
C = new EnumExample("C", 2)
D = new EnumExample("D", 3)
$VALUES = (new EnumExample[] { A, B, C, D })
Methode: public static EnumExample[] values()
Methode: public static EnumExample valueOf(java/lang/String) Die Untersuchung von EnumExample.class zeigt, wie der Compiler
Listing 1.44 auswertet bzw. welche Bytecoderepräsentation
ein Aufzählungstyp besitzt. Aufgrund der durch die Bytecodeanalyse erhaltenen
Informationen können die folgenden Aussagen getroffen werden:
-
Der Aufzählungstyp besitzt den Klassenmodifier final und
ist mit dem Property-Modifier enum versehen. In der Klassendatei
wird ein Bit (ACC_ENUM ) an entsprechender Stelle gesetzt, falls eine
Klasse als Aufzählungstyp deklariert ist.
-
Die Deklaration eines Aufzählungstyps definiert eine Klasse,
deren direkte Superklasse die Klasse Enum ist. Enum
befindet sich im Paket java.lang und ist die Basisklasse aller
Aufzählungstypen. Die Klasse Enum erweitert wiederum die Wurzelklasse
Object . Eine direkte Ableitung von Enum mit Hilfe der
extends -Klausel ist nicht möglich und würde zu einer entsprechenden
Fehlermeldung des Compilers führen ("classes cannot directly extend
java.lang.Enum ").
-
Die aus Listing 1.44 erzeugte Klassendatei enthält
Bytecode für fünf Felder. Für die Enum-Konstanten A , B ,
C und D wird durch den Compiler ein entsprechendes
Feld generiert. Die vier Felder besitzen jeweils die Modifier public ,
static und final . Zusätzlich ist innerhalb der
Klassendatei jeweils das Bit ACC_ENUM (Property-Flag) für diese
Felder gesetzt. Das gesetzte Bit zeigt an, dass das Feld für das Speichern einer
Instanz des Aufzählungstyps vorgesehen ist. Das Feld $VALUES wird
durch den Compiler zusätzlich erzeugt und ist deshalb mit synthetic
bzw. einem gesetzten Bit (ACC_SYNTHETIC ) innerhalb der Klassendatei
versehen. $VALUES wird zur Realisierung des Aufzählungstyps
zusätzlich benötigt.
-
Der Java-Compiler fügt weiterhin einen Konstruktor in die
.class -Datei ein. Er ist mit dem Modifier private
deklariert und besitzt zwei Parameter mit den Typen String und
int . Die Aufgabe des automatisch generierten Konstruktors ist es,
den entsprechenden Konstruktor der direkten Superklasse Enum
aufzurufen - dies geschieht mit dem Schlüsselwort super .
API 1.5
java.lang
Class Enum<E extends Enum<E>>
Konstruktor:
Enum(String name, int ordinal) Der genannte Kontruktor der Klasse Enum kann nur "intern"
aufgerufen werden (sole constructor). Der Aufruf des Konstruktors erfolgt
im Zusammenhang mit der Realisierung von Aufzählungstypen. Er besitzt die beiden
Parameter name und ordinal . Dem Konstruktor können
Name (z.B. A ) und Ordinalzahl (Ordnungszahl) der jeweiligen
Enum-Konstanten übergeben werden. Die Ordnungzahl gibt dabei die Position
der Enum-Kontanten innerhalb der Deklaration des Aufzählungstypen an. Im Beispiel
hat die Enum-Konstante A die Ordnungszahl 0 und die Ordnungszahl
1 bezieht sich auf B
(der Namensbezeichnung Enum folgt innerhalb de API-Dokumentation
ein Zusatz innerhalb von spitzen Klammern, siehe dazu Abschnitt 3
über Generics).
-
Die Klassendatei EnumExample.class enthält weiterhin einen
statischen Initialisierer. Darin werden die Enum-Konstanten A ,
B , C und D initialisiert. Die Initialisierung
erfolgt mit Hilfe des Schlüsselwortes new und einem entsprechenden
Konstruktoraufruf. Dem Konstruktor werden jeweils zwei Argumente übergeben.
Das erste Argument ist vom Typ String und ist die Namensbezeichnung
der Enum-Konstanten (z.B. "A" ). Als zweiter Wert wird die jeweilige
Ordnungszahl der Enum-Konstanten übergeben. $VALUE wird mit einem
EnumExample -Array initialisiert. Die Elemente des Arrays sind
innerhalb der geschweiften Klammern enthalten und sind im Beispiel die
Enum-Konstanten A , B , C und D .
-
Durch Compilierung des Beispiels EnumExample.java werden
in die erzeugte Klassendatei automatisch die zwei zusätzlichen Methoden
values und valueOf geschrieben. Die statischen Methoden
geben Werte vom Typ EnumExample[] bzw. EnumExample zurück.
Sie sollen an späterer Stelle mit Hilfe von Listing 1.47
erläutert werden.
1.11.3. Enum-Konstanten mit Argumenten
Es besteht die Möglichkeit Enum-Konstanten mit Argumenten zu deklarieren.
Der Aufzählungstyp aus Listing 1.44 soll erweitert
werden, indem den darin enthaltenen Enum-Konstanten A , B ,
C und D jeweils zwei Argumente zugeordnet werden.
Die Argumente einer Enum-Konstanten können innerhalb runder Klammern notiert
werden.
EnumKonstanteName(ArgumentListe_opt) Mehrere Argumente werden durch ein Komma voneinander getrennt.
Eine mögliche Erweiterung von EnumExample.java könnte angegeben
werden zu:
/* Test.java */
public enum Test {
A(1, 2), B(3, 4), C(5, 6), D(7, 8);
private Test(int value1, int value2) {
}
} Die Enum-Konstante A besitzt z.B. als Argumente die Zahlen 1
und 2. Das kurze Testprogramm enthält weiterhin einen Konstruktor, dessen
Parameteranzahl mit der Anzahl der Argumente der Enum-Konstanten
übereinstimmt. Die Konstruktordeklaration ist notwendig, da ansonsten bei
der Compilierung des Quelltextes eine Fehlermeldung erfolgt. Der Java-Übersetzer
javac erzeugt zu dem im Quelltext stehenden Konstruktor einen
"passenden" Konstruktor innerhalb der zugehörigen Klassendatei. Der
"Bytecode-Konstruktor" enthält insgesamt vier Parameter - zusätzlich einen
Parameter vom Typ String und einen vom Typ int . Der
zusätzliche int -Parameter ist für die Ordinalzahl vorgesehen
(siehe dazu Abschnitt 1.11.2). Der in Test.java
angegebene bzw. notwendige Konstruktor ist ein Resultat des "Konzepts
Aufzählungstyp". Der Konstruktor kann genutzt werden, um die erhaltenen
beiden Argumente in entsprechende Felder zu speichern. Das folgende Listing
enthält zusätzlich noch die beiden Methoden getValue1 und
getValue2 , mit denen die Werte der Felder value1 und
value2 abgefragt werden können. Für die Werte dieser beiden Felder
gilt: Sie sind Argumentwerte der Enum-Konstanten. Z.B. könen die Werte der
Felder 1, 2 oder 3, 4 sein, abhängig von der Enum-Konstanten A oder
B .
Listing 1.46. EnumExample3.java . Deklaration des Aufzählungstyps
EnumExample3 . Die Enum-Konstanten A , B ,
C und D besitzen jeweils zwei Argumente.
public enum EnumExample3 {
A(1, 2), B(3, 4), C(5, 6), D(7, 8);
private final int value1;
private final int value2;
private EnumExample3(int value1, int value2) {
this.value1 = value1;
this.value2 = value2;
}
public int getValue1() {
return value1;
}
public int getValue2() {
return value2;
}
}
1.11.4. Implizite Methoden values und valueOf
Ein Aufzählungstyp, z.B. mit dem Namen E , deklariert implizit
zwei statische Methoden. Die beiden Methoden values und
valueOf werden einem erstellten Aufzählungstypen automatisch
hinzugefügt (der Compiler übersetzt den Quelltext des Typen in eine Klassendatei,
wobei der erzeugten Klassendatei diese zusätzlichen Methoden hinzugefügt werden).
static E[] values();
static E valueOf(String name); Die Methode values gibt ein Array zurück, dessen Elemente
die Enum-Konstanten des Aufzählungstyps sind. Durch Aufruf der statischen
Methode valueOf wird diejenige Enum-Konstante zurückgegeben, die
mit dem Parameter name spezifiziert ist. Der anschließende
Quelltext beinhaltet diese beiden speziellen Methoden. Das Beispiel verwendet die
Aufzählungstypen aus Listing 1.44 und
Listing 1.46.
Listing 1.47. EnumExample4.java . Verwendung der Methoden values
und valueOf .
public class EnumExample4 {
public static void main(String[] args) {
EnumExample[] ee = EnumExample.values();
for (int i = 0; i < ee.length; i++) {
System.out.println(ee[i]);
}
EnumExample3[] ee2 = EnumExample3.values();
for (int j = 0; j < ee2.length; j++) {
System.out.println(ee2[j] + ": " +
ee2[j].getValue1() + ", " +
ee2[j].getValue2());
}
EnumExample3 ee3 = EnumExample3.valueOf("B");
System.out.println(ee3.getValue1() + ", " + ee3.getValue2());
}
}
Nach Ausführung des Programms kann die folgende Ausgabe festgestellt
werden:
A
B
C
D
A: 1, 2
B: 3, 4
C: 5, 6
D: 7, 8
3, 4 Die ersten vier Zeilen ergeben sich in Bezug auf den Aufzählungstyp
EnumExample und beinhalten die Namensbezeichnungen der darin
deklarierten Enum-Konstanten. Die anschließenden Zeilen beinhalten
Informationen des Aufzählungstyps EnumExample3 . In der letzten
Zeile stehen die beiden Argumentwerte der Enum-Konstanten B , die
mit Hilfe des Methodenaufrufs valueOf("B") abgefragt werden
konnten.
|