6.14 Die Spezial-Oberklasse Enum
 
Jedes Aufzählungs-Objekt erbt von der Spezialklasse Enum. Nehmen wir erneut die Musikstile:
enum Stil { ROCK, POP, TECHNO }
Der Compiler übersetzt dies in eine Klasse, die in etwa beginnt mit:
class Stil extends Enum
{
public static final Stil ROCK = new Stil( "ROCK", 0 );
public static final Stil POP = new Stil( "POP", 1 );
public static final Stil TECHNO = new Stil( "TECHNO", 2 );
Stil( String s, int i )
{
super( s, i );
}
6.14.1 Methoden auf Enum-Objekten
 
Von der Oberklasse Enum erbt jede Aufzählung einen geschützten parametrisierten Konstruktor, der den Namen der Konstante sowie einen assoziierten Zähler erwartet. So wird aus jedem Element der Aufzählung ein Objekt vom Basistyp Enum, welches einen Namen und eine ID, die so genannte Ordinalzahl, speichert. Natürlich kann es auch nach seinem Namen und Zähler gefragt werden.
abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable
|
|
final int ordinal()
Liefert die zur Konstante zugehörige ID. Im Allgemeinen ist diese Ordinalzahl nicht wichtig, aber besondere Datenstrukturen wie EnumSet oder EnumMap nutzen diese eindeutige ID. Die Reihenfolge der Zahlen ist durch die Reihenfolge der Definition gegeben. |
|
final String name()
Liefert den Namen der Konstanten. Da die Methode – wie viele anderen der Klasse – final ist, lässt sich der Name nicht ändern. |
|
String toString()
Liefert den Namen der Konstanten. Die Methode ruft standardisiert name() auf, weil sie aber nicht final ist, kann sie überschrieben werden. |
|
final int compareTo( E o )
Da die Enum-Klasse die Schnittstelle Comparable implementiert, gibt es auch die Funktion compareTo(). Sie vergleicht anhand der Ordinalzahlen. Vergleiche sind nur innerhalb eines Enum-Typs erlaubt. |
|
static <T extends Enum<T>> T valueOf( Class<T> enumType, String s )
Ermöglicht das Suchen von Enum-Objekten zu einem Konstantennamen und einer Enum-Klasse. Sie liefert das enum-Objekt für die gegebene Zeichenfolge oder löst eine IllegalArgumentException aus, wenn dem String kein enum-Objekt zuzuordnen ist. |
|
final Class<E> getDeclaringClass()
Liefert das Class-Objekt zu diesem Enum. |
Beispiel Eine Funktion, die die Ordinalzahl gibt, oder –1, wenn es die Konstante nicht gibt.
static int gibOrdinal( String name )
{
try {
return Stil.valueOf( name ).ordinal();
}
catch ( IllegalArgumentException e ) {
return –1;
}
}
Damit liefert gibOrdinal("POP") == 1 und gibOrdinal("CLASSIC") == –1.
|
Alle Konstanten der Klasse aufzählen
Eine besondere statische Funktion auf jeder Enum-Klasse ist values(). Sie liefert ein Feld von Enum-Objekten. Nützlich ist das für das erweiterte for, welches alle Konstanten aufzählen soll. Eine Alternative mit dem gleichen Ergebnis ist die Class-Methode getEnumConstants().
enum Stil { ROCK, POP, TECHNO }
for ( Stil e : Stil.values() ) // oder Stil.class.getEnumConstants()
System.out.println( "Name=" + e.name() );
Liefert Zeilen mit Name=ROCK, Name=POP, Name=TECHNO.
Auch toString() ist so implementiert, dass nur der Name ausgegeben wird. Das Ergebnis ist also mit name() identisch. Daher liefert
System.out.println( Stil.ROCK ); // auch "ROCK"
Vergleiche mit ==
Wie die Umsetzung der Enum-Typen zeigt, wird für jede Konstante ein Objekt konstruiert, und das sind Singletons. Der Zugriff auf dieses Objekt ist wie ein Zugriff auf eine statische Variable. Der Vergleich zweier Konstanten läuft somit auf den Vergleich von statischen Referenzvariablen hinaus, wofür der Vergleich mit == richtig ist. Ein equals() ist nicht nötig. Die Oberklasse Enum überschreibt equals() mit der Logik wie in Object – also den Referenzvergeleich –, um sie final zu markieren. Die Methode clone() ist final protected und kann also weder überschrieben noch von außen aufgerufen werden, so dass es Kopien der Enum-Objekte geben kann, die die Identität gefährden könnten. Nichts desto trotz darf clone() aber die this-Referenz liefern.
6.14.2 enum mit eigenen Konstruktoren und Methoden
 
Da eine emum-Klasse die Klassendefinition erweitert, kann sie zusätzlich Attribute und Methoden bekommen.
Listing 6.82
Country.java
import java.util.Locale;
public enum Country
{
GERMANY( Locale.GERMANY ), UK( Locale.UK ), CHINA( Locale.CHINA );
private Locale country;
private Country( Locale country )
{
this.country = country;
}
public String getISO3Country()
{
return country.getISO3Country();
}
}
Bei der Deklaration der Konstanten wird in runden Klammern ein Argument für den Konstruktor aufgerufen. Der speichert das zugehörige Locale-Objekt in der internen Variablen country. Zusätzlich definiert das Beispiel eine Methode getISO3Country(), die auf den Enum-Objekten aufgerufen werden kann.
System.out.println( Country.CHINA.getISO3Country() ); // CHN
Da switch auf enum erlaubt ist, können wir schreiben:
Listing 6.83
CountryEnumDemo.java, Ausschnitt
Country meinLand = Country.GERMANY;
switch ( meinLand )
{
case GERMANY:
System.out.println( "Aha. Ein Krauti" ); // Aha. Ein Krauti
System.out.println( meinLand.getISO3Country() ); // DEU
break;
default: System.out.println( "Anderes Land" );
}
Enum mit überschriebenen Methoden
Definieren wir für Monate Aufzählungen in der Enum Month und bieten eine Methode getDays() für die Anzahl Tage im Monat an, dann hätten wir ein Problem mit dem Februar, weil die Anzahl der Tage bei einem Schaltjahr nicht die gleiche ist. In getDays() würden wir eine Berechnung abhängig vom Jahr benötigen. Im ersten Schritt fügen wir der Aufzählung eine Funktion getDays(int) hinzu, die die Jahreszahl akzeptiert. Die Methode ist zunächst genauso ohne Parameter implementiert wie getDays(). Java erlaubt uns aber, hinter den Konstantennamen eine Art innere anonyme Klasse zu hängen, die dann Methoden überschreiben kann.
Listing 6.84
Month.java
public enum Month
{
JAN(31), FEB(28)
{
@Override public int getDays( int y )
{
return (y & 3) == 0 ? 29: 28;
}
},
MAR(31), APR(30), MAY(31), JUN(30),
JUL(31), AUG(31), SEP(30), OCT(31), NOV(30), DEC(31);
private int days;
private Month( int days )
{
this.days = days;
}
public int getDays()
{
return days;
}
public int getDays( int year )
{
return days;
}
}
In der Definition vom Februar überschreiben wir das Verhalten von getDays(int), sodass korrekt ausgegeben wird:
System.out.println( Month.FEB.getDays(2000) ); // 29
System.out.println( Month.FEB.getDays(2001) ); // 28
In der Klassendatei bildet der Eclipse-Compiler das wie folgt ab: Es gibt einen Konstruktor mit drei Parametern. Parametern der Enum-Namen, der Ordinalzahl und der Zahl der Tage.
Month( String s, int i, int j )
{
super( s, i );
days = j;
}
Für alle Monate bis auf den Februar kommt dieser Konstruktor zum Zuge. Für den Februar gibt es eine innere anonyme Klasse, die getDays() überschreibt, um das Jahr mit in die Berechung einzubeziehen. Die übergebene 28 würde eigentlich im Konstruktor nicht mehr gebraucht werden.
public static final Month JAN = new Month( "JAN", 0, 31 );
public static final Month FEB = new Month( "FEB", 1, 28 ) {
private static final _cls1 ENUM$VALUES[];
public int getDays( int y ) { return (y & 3) != 0 ? 28 : 29; }
};
public static final Month MAR = new Month( "MAR", 2, 31 );
...
|