|
|||||||||||||||||||
HOME | COURSES | TALKS | ARTICLES | GENERICS | LAMBDAS | IOSTREAMS | ABOUT | CONTACT | | | | |||||||||||||||||||
|
Aufzählungstypen - Enumeration Types
|
||||||||||||||||||
Die Version 5.0 des JDK wurde im September 2004 freigegeben und hat
die Java-Entwickler mit allerlei neuen Sprachmitteln beglückt.
In diesem und den nächsten Beiträgen in dieser Kolumne wollen
wir uns die wichtigsten Neuerungen ansehen und dabei – wie immer in dieser
Kolumne – auf die Stolpersteine eingehen. Wir beginnen die Serie
mit einer Einführung in die Aufzählungstpyen.
HistorieEnum-Typen sind Aufzählungstypen von unveränderlichen Werten. Solche Aufzählungstypen gab es bereits in den Vorgängersprachen C und C++, von denen Java einiges geerbt hat - allerdings nicht die Aufzählungstypen. Beim Design der Sprache Java war man der Meinung, daß Aufzählungstypen ein überflüssiges Sprachmittel seien. Unveränderliche Werte kann man schließlich auch durch final-Konstanten ausdrücken. Also wozu braucht man Enum-Typen? Mit der Zeit hat sich jedoch herausgestellt, daß Aufzählungstypen in der Praxis recht häufig vorkommen und viele Entwickler haben versucht, einen Aufzählungstypen als Klasse mit final-Feldern auszudrücken. Spätestens seit Joshua Bloch in seinen populären Buch "Effective Java" (das zufälllig den Titel unserer Kolumne trägt ;-) eine solche "Enum-Klasse" beschrieben hat, ist diese Technik allgemein etabliert. Für die Version 5.0 von Java haben Joshua Bloch und andere die Einbettung von Aufzählungstypen in die Sprache vorgeschlagen und spezifiziert. Deshalb gibt es nun auch in Java neben Klassen und Interfaces auch noch Enum-Typen.Einfache Enum-TypenIm einfachsten Fall ist ein Enum-Typ eine Aufzählungstypen von unveränderlichen Werten, die beispielsweise so aussehen könnte:public enum Season { winter, spring, summer, fall }enum ist ein neues Schlüsselwort in der Sprache und dient dazu, dem Compiler die Definition eines neuen Typs bekannt zu geben. Es ist vergleichbar mit den Schlüsselworten class und interface, die ebenfalls die Definition von neuen Typen einleiten. Der im obigen Beispiel definierte Typ heißt Season und es gibt genau 4 Objekte von diesem Typ, nämlich die Objekte winter, spring, summer und fall. Weitere Objekte vom Typ Season können und sollen nicht erzeugt werden. Alle 4 Objekte repräsentieren "Konstanten", d.h. die Objekte sind unveränderlich - jedenfalls vom Konzept her. Dass sie nicht wirklich unveränderlich sind, sehen wir später noch, wenn wir die Details anschauen. Solche Aufzählungen von Werten hat man traditionell in Java mit static final int-Werten implementiert. Das sieht dann so aus: public class Season {Das Ergebnis ist ebenfalls ein Typ Season mit 4 konstanten Werten winter, spring, summer und fall. In beiden Fällen kann man switch-Anweisungen schreiben mit Zweigen für die 4 Werte. Die Anweisungen sehen fast identisch aus. Traditionell sähe es so aus: public String getGermanName(int season) {Mit den neuen Enum-Typen sieht es so aus: public String getGermanName(Season season) {Bislang konnte ein switch nur über eine int-Variable gemacht werden. In Java 5.0 sind auch enum-Variablen erlaubt. Außerdem entfällt der lästige default-Zweig, wenn es zu jedem Enum-Wert eine korrespondierende Funktionalität gibt. Das ist anders als bei der traditionellen Lösung, bei der die emulierten Enum-Werte in Wirklichkeit von Integer-Zahlen repräsentiert werden. Der Methode getGermanName(int season) könnte beispielsweise die Zahl 10 übergeben werden, die keiner der definierten Season-Konstanten entspricht. Das kann bei den neuen Enum-Typen nicht vorkommen. Die Enum-Typen haben noch allerlei andere Vorzüge.
Ein Blick hinter die KulissenWir wollen an dieser Stelle gar nicht auf alle Eigenschaften der neuen Enum-Typen eingehen, sondern statt dessen einmal näher ansehen, was der Compiler aus einem Enum-Typ macht. Das ist wichtig, um einige der Überraschungen zu verstehen, die man mit Enum-Typen erleben kann.
Der Compiler übersetzt die Definition eines Enum-Typen in eine
Klassendefinition. Im Byte-Code findet man deshalb keinen Enum-Typ
Season, sondern eine Klasse Season. Diese synthetische Klasse sieht
so oder so ähnlich aus, wie in Listing 1 gezeigt. Was der Compiler
generiert, ist compiler-abhängig und kann im Detail von der hier gezeigten
Implementierung abweichen.
Wie man sieht, sind alle aus Enum-Typen generierten Klassen von einer
generischen Superklasse Enum abgeleitet, von der sie einige Methoden erben.
Die Superklasse java.lang.Enum ist in Listing 2 gezeigt (obwohl man diese
Klasse nicht direkt nutzen kann, ist sie auch in der JavaDoc des JDK beschrieben).
Wie man sieht, ist der Enum-Typ Season ein Referenztyp und alle Enum-Konstanten sind Objekte vom Typ Season. An der Superklasse Enum kann man sehen, daß jedes Enum-Objekt aus einem String besteht, dem symbolischen Namen, und einem int-Wert, seiner Ordinalzahl. Die vier Season-Objekte werden über statische Felder in der Klasse Season referenziert und diese Referenzen werden in einem Static-Initializer-Block initialisiert. Es gibt also tatsächlich nur vier statische Season-Objekte. Es können keine weiteren Season-Objekte erzeugt werden. Der Konstruktor der Klasse Season ist private und kann deshalb von Außen nicht aufgerufen werden. Er wird lediglich intern für die Erzeugung der vier statischen Objekte verwendet. Die clone()-Methode ist in der Superklasse Enum implementiert und wirft immer eine Exception. Sie ist darüber hinaus als final deklariert, so daß diese Implementierung nicht überschrieben werden kann. Enum-Objekte können also vom Benutzer weder konstruiert noch geklont werden. Außerdem ist die generierte Klasse final, so man auch durch ableiten bzw. überschreiben kein anderes Verhalten einbringen kann. Infrastruktur.Methoden wie toString(), equals(), hashcode() und compareTo() sind bereits in der Enum-Superklasse definiert. Daneben gibt es noch eine Reihe von Hilfsmethoden wie zum Beispiel eine values()-Methode, die ein Array mit allen Enum-Werten eines Enum-Typs liefert. Komplexere Enum-TypenNeben den ganz einfachen Aufzählungstypen wie unserem Season-Typ gibt es wesentlich komplexere Enum-Typen. Java erlaubt es nämlich, dass Enum-Typen Felder und Methoden haben können. Hier ist ein einfaches Beispiel:public enum Month {Der Enum-Typ Month hat ein Feld, welches die Anzahl der Tage in jedem Monat enthält. Natürlich ist das Beispiel grob vereinfacht; wir haben die Schaltjahre schlicht ignoriert. Das Feld days muß initialisiert werden. Zu diesem Zweck hat der Enum-Typ Month einen Konstruktor, der die Anzahl der Tage zum Parameter hat. Dieser Konstruktor ist private, sonst könnten ja außerhalb des Enum-Types explizit neue Month-Objekte erzeugt werden und das soll verhindert werden. Der Konstruktor wird aufgerufen in der Aufzählung der einzelnen Enum-Werte. Hinter dem jeweiligen symbolischen Namen folgen in runden Klammern das Argument bzw. die Argumente des Konstruktors. Das heißt, in dem Ausdruck january(31) ist january der symbolische Name des Enum-Werts und 31 ist das Argument des Konstruktors. Daneben gibt es eine public Zugriffsmethode days(), die den Wert des days-Feldes liefert. In unserem Beispiel ist die Zugriffsmethode eine rein lesende Methode, die das Month-Objekt nicht modifiziert. Das ist typisch für die Methoden von Enum-Typen, weil Enum-Werte konzeptionell unveränderliche Objekte sind. Diese Unveränderlichkeit wird jedoch nicht erzwungen. Prinzipiell wäre es möglich, daß ein Enum-Typ beliebig viele Felder hat und beliebige Methoden, die diese Felder ändern. Dann wären die einzelnen Enum-Werte veränderlich und keine "Konstanten" mehr. Das widerspricht zwar der eigentlichen Idee von Enum-Werten als symbolischen Konstanten, ist aber erlaubt. Enum-Typen können auch noch komplexer sein und je Enum-Wert eine andere Implementierung für eine bestimmte Methode haben. Das könnte dann so aussehen: public enum Operation {Dieser Enum-Typ Operation hat 4 Werte: plus, minus, times und div und eine abstrakte Methode eval(). Jeder der Enum-Werte implementiert die abstrakte Methode auf eine andere Art und Weise. In der nachfolgenden Methode würde dann für jeden Aufruf von eval()etwas anderes heraus kommen: void f(double x, double y) {Intern in der generierten Klasse ist jeder der Enum-Werte von einem anderen Typ, in dem die Methode eval() jeweils anders implementiert ist. Diese vier verschiedenen Typen sind in der Regel anonyme innere Klassen.
Wenn man also das Objekt Operation.minus mit getClass() nach seinem Type fragen würde, dann hätte es einen anderen Typ als das Objekt Operation.plus und beide Typen sind vom zugehörigen Enum-Typ Operation verschieden. Wenn man wissen will, zu welchem Enum-Typ die Objekte Operation.plus und Operation.minus gehören, muß man die Methode getDeclaringClass() anstelle der Methode getClass() verwenden. getDeclaringClass() ist eine der Methoden, die der Compiler automatisch für die generierte Enum-Klasse erzeugt. Enum-Typen mit abstrakten Methoden haben wir in der Praxis noch nie gebraucht. Hingegen ist die Möglichkeit, einem Enum-Typ Felder zu geben und entsprechende Zugriffsmethoden zu definieren, in der Praxis recht nützlich und wird relativ häufig verwendet. Allerdings ist das Definieren und Verwenden solcher Enum-Typen bisweilen mit Überraschungen verbunden. Ein solchen Beispiel mit Überraschungen sehen wir uns in der nächsten Ausgabe der Kolumne an. ZusammenfassungDie Enum-Typen sind eines der in Java 5.0 neuen Sprachmittel. Ein Enum-Typ ist ein Typ, der eine fixe Anzahl von symbolischen Konstanten definiert. Solche Aufzählungstypen, von denen es nur eine handvoll Objekte gibt, kommen in der Praxis relativ häufig vor. Ein Beispiel im JDK ist java.util.concurrent.TimeUnit - ein Typ der Zeiteinheiten wie Nanosekunden, Millisekunden, Sekunden, usw. repräsentiert.Enum-Typen in Java können relativ komplex sein. Sie können Felder und Methoden haben und sogar unterschiedliche Implementierungen von abstrakten Methoden je Enum-Werte haben. Literaturverweise und weitere Informationsquellen
Die gesamte Serie über Enum-Typen:
|
|||||||||||||||||||
© Copyright 1995-2012 by Angelika Langer. All Rights Reserved. URL: < http://www.AngelikaLanger.com/Articles/EffectiveJava/28.Enums/28.Enums.html> last update: 4 Nov 2012 |