Advanced
Java
Services
|
Enums |
Es hat lange gedauert, bis die Java-Entwickler sich entschlossen haben Enumerationen
in Java einzuführen. Lange Zeit hielt man sie für überflüssig, doch durch die Konkurrenz
von C# hat man sich nun doch durchgerungen Enumerationen einzuführen.
Im Gegensatz zu C++ und C# steht das in 1.5 bzw. 5.0 eingeführte Schlüsselwort enum für
eine spezielle Form einer Klasse. Die Enum-konstanten sind damit nicht einfach primitive int-
Konstanten wie in anderen Sprachen, sondern Objekte vom Typ der enum-Klasse.
Schauen wir uns zum Vergleich mal Enumerationen in C/C++ an:
enum Days
{
Mon, Tue, Wed, Thu, Fri, Sat, Sun
} ;
Mit der obigen Vereinbarung wird ein neuer Datentyp Days geschaffen. Dieser hat einen Wertebereich,
der aus den sieben Konstanten besteht, die in der Klammer aufgeführt werden. Hinter den Konstanten
verbergen sich Zahlen vom Typ int, die Zählung ist fortlaufend und beginnt bei 0. Wenn man will, kann man
die Zählung auch mit einer anderen Zahl beginnen lassen:
enum Days
{
Mon=1, Tue, Wed, Thu, Fri, Sat, Sun
} ;
Die Verwendung des Typs Days kann etwa so aussehen:
int main(int argc, char *argv[])
{
Days day = Sat ;
cout << Tue << endl; // Ausgabe: 2
cout << Sat << endl; // Ausgabe: 6
int x = Sat; // OK
short y = Sun; // OK
unsigned z = Mon; // OK
//day = x ; // NOK: invalid conversion
}
Um obige main-Funktion zum Laufen zu bringen braucht man natürlich noch ein include und die Angabe eines namespace. Man sieht deutlich, daß hier enums im wesentlichen integer-Konstanten sind. Eine Typprüfung findet statt, der Compiler erlaubt eine Zuweisung nur in einer Richtung. Die letzte auskommentierte Zuweisung ist ja auch gefährlich, könnte man so der Variablen day auch Werte zuweisen, die garnicht im Wertebereich von Days liegen.
In Java definiert man mit enum eine spezielle Form einer Klasse. Es zeigt sich hier wieder einmal, was für ein flexibler Begriff eine Klasse ist und was man damit alles modellieren kann. Damit geht man in Java einen großen Schritt weiter, denn in den anderen Sprachen sind Enumerationen lediglich, wie es so schön heißt "glorified integers".
Dabei sieht es zunächst fast genauso aus wie im obigen Beispiel:
enum Season
{
SPRING, SUMMER, AUTUMN, WINTER ;
}
Der Compiler compiliert das zu einer Klasse Season.class . Schaut man sich mit Hilfe von Reflection
oder einfacher mit einem Decompiler an, was der Compiler aus diesen drei Zeilen macht, so wird der
Unterschied zu C/C++ oder C# deutlich:
final class Season extends Enum
{
public static final Season SPRING;
public static final Season SUMMER;
public static final Season AUTUMN;
public static final Season WINTER;
private static final Season VALUES[];
static
{
SPRING = new Season("SPRING", 0);
SUMMER = new Season("SUMMER", 1);
AUTUMN = new Season("AUTUMN", 2);
WINTER = new Season("WINTER", 3);
VALUES = new Season[] { SPRING, SUMMER, AUTUMN, WINTER } ;
}
private Season(String s, int i)
{
super(s, i);
}
public static final Season[] values()
{
return (Season[])VALUES.clone();
}
public static Season valueOf(String s)
{
return (Season)Enum.valueOf(Season, s);
}
}
Die Enumkonstanten werden also als statische Konstanten angelegt und in einem static initializer
ins Leben herufen. Zusätzlich wird ein statisches Array angelegt. Ein clone dieses Arrays wird
über die statische Methode values() zurückgeliefert. Diese Methode wird vom Compiler automatisch
generiert. Ebenso automatisch generiert wird die zweite statische Methode valueOf(), die sich auf
die namensgleiche statische Methode valueOf() aus der Klasse Enum stützt. Da die Elternklasse Enum
nur über den protected Konstruktor Enum(String name, int ordinal) verfügt, muß es in der Ableitung
einen Konstruktor geben, der einen entsprechenden super-Aufruf enthält. Dieser Konstruktor wird als
private-Konstruktor generiert, d.h. in Falle eines enum generiert der Compiler keinen default-Konstruktor
sondern einen Konstruktor mit zwei Parametern:
private Season(String s, int i)
{
super(s, i);
}
Das folgende Beispiel zeigt eine ganze Reihe von weiteren Eigenschaften und Möglichkeiten.
Die enum-Klasse Borders hat einen erweiterten Datenteil. Dieser Datenteil kann über einen
zusätzlichen Konstruktor gesetzt werden, ist aber auch über set/get-Methoden manipulierbar.
Dieser Konstruktor kann nur innerhalb der Klasse für die Initialisierung der Konstanten
verwendet werden. Eine Verwendung mit new ist nicht möglich.
Natürlich müssen Enumerationen zusammen mit switch verwendet werden können. Deshalb mußten die
Java-Entwickler die möglichen Typen für switch um Enumklassen erweitern. Das folgende Beispiel
zeigt dies.
public class EnumDemo
{
private enum Borders
{
LEFT(6) , TOP(8), RIGHT(6), BOTTOM(4) ;
private int size ;
Borders(int a)
{
size = a ;
}
public int getSize()
{
return size ;
}
public void setSize(int w)
{
size = w ;
}
}
public static void main(String args[])
{
//Borders bo = new Borders(); enum types may not be instantiated
Borders bor = Borders.TOP ;
System.out.println(bor.getSize() );
bor.setSize(10);
System.out.println(bor.getSize() );
switch(bor)
{
case LEFT : System.out.println("left border");
default: System.out.println("no left border");
}
}
}
Des weiteren wurde die Klasse Borders als innere Klasse angelegt. Aufmerksamen Lesern wird aufgefallen sein,
daß die rot hervorgehobene Zeile keine Fehlermeldung erzeugt. Folgerung: Innere enum-Klassen werden als
static nested angelegt. Ein Blick auf das Decompilat bestätigt dies.
private static final class EnumDemo$Borders extends Enum
{
public static final EnumDemo$Borders LEFT;
public static final EnumDemo$Borders TOP;
public static final EnumDemo$Borders RIGHT;
public static final EnumDemo$Borders BOTTOM;
private static final EnumDemo$Borders $VALUES[];
private int size;
static
{
LEFT = new EnumDemo$Borders("LEFT", 0, 6);
TOP = new EnumDemo$Borders("TOP", 1, 8);
RIGHT = new EnumDemo$Borders("RIGHT", 2, 6);
BOTTOM = new EnumDemo$Borders("BOTTOM", 3, 4);
$VALUES = (new EnumDemo$Borders[] { LEFT, TOP, RIGHT, BOTTOM });
}
public static final EnumDemo$Borders[] values()
{
return (EnumDemo$Borders[])$VALUES.clone();
}
public static EnumDemo$Borders valueOf(String s)
{
return (EnumDemo$Borders)Enum.valueOf(EnumDemo$Borders, s);
}
private EnumDemo$Borders(String s, int i, int j)
{
super(s, i);
size = j;
}
public int getSize()
{
return size;
}
public void setSize(int i)
{
size = i;
}
}
Vererbungshierarchie für eine selbstgeschriebene enum-Klasse