6.5 Konstanten und Aufzählungen
 
6.5.1 Konstanten mit dem Schlüsselwort final bei Variablen
 
In Programmen gibt es Variablen, die sich ändern wie ein Schleifenzähler, aber auch andere, die sich beim Ablauf eines Programms nicht ändern. Dazu gehören etwa die Anzahl Buchstaben in ›Nichts als die Wahrheit‹ – zumindest in der ersten Ausgabe – oder die Ausmaße einer DIN-A4-Seite1
. Die Werte sollten nicht wiederholt im Quellcode stehen, sondern über ihren Namen angesprochen werden. Dazu werden Variablen definiert, denen genau der konstante Wert zugewiesen wird; die Konstanten heißen dann symbolische Konstanten.
Statische Variablen werden auch verwendet, um symbolische Konstanten zu definieren. Damit die Variablen unveränderlich bleiben, gesellt sich zusätzlich der Modifizier final dazu. Dem Compiler wird auf diese Weise mitgeteilt, dass dieser Variablen nur einmal ein Wert zugewiesen werden darf. Für Variablen bedeutet dies: Es sind Konstanten, jeder spätere Schreibzugriff wäre ein Fehler.
Listing 6.14
Genre.java
public class Genre
{
public final static int CHRISTIAN_GANGSTA_RAP = 0;
public final static int COUNTRY = 1;
public final static int GANGSTA = 2;
public final static int JUNGLE = 3;
public final static int JPOP = 4;
public final static int GOTHIC_TECHNO = 5;
public final static int POLKA = 6;
public final static int PSYCHEDELIC_CLASSIC = 7;
public final static int TECHNO = PSYCHEDELIC_CLASSIC + 1;
}
In der Klasse Genre werden mehrere Konstanten definiert. Für Konstanten ist es günstig, sie relativ zum Vorgänger zu wählen, um das Einfügen in der Mitte zu vereinfachen. Das sehen wir bei der letzen Variable TECHO. Da im Quellcode das Vorkommen der Zahlen 2, 4, 5 undurchsichtig wäre, sind symbolische Namen zwingend. Stehen dennoch Zahlen ohne öffentliche Bedeutung im Quellcode, so werden sie magische Zahlen (engl. magic numbers) genannt.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 6.4
Die Genre-Klasse in einer alternativen ‚Eclipse-Darstellung’
Tipp Es ist eine gute Idee, die Namen von Konstanten vollständig großzuschreiben, um deren Bedeutung hervorzuheben.
|
6.5.2 Problem mit finalen Klassenvariablen
 
Finale Klassenvariablen können in der Entwicklung mit einer größeren Anzahl von Klassen zu einem Problem werden. Das liegt an der Eigenschaft der finalen Werte, dass sie sich nicht ändern können und sich daher sicher an der Stelle einsetzen lassen, wo sie gebraucht werden. Ein Beispiel:
class Finanz
{
public final static int MWST = 16;
}
Greift eine andere Klasse auf die Variable MWST zu, so ist das im Quellcode nicht als direkter Variablenzugriff Finanz.MWST kodiert, sondern das Literal 16 wird direkt an der Aufrufstelle eingesetzt. Das ist eine Optimierung des Compilers, die er laut Java-Definition machen kann.
Das ist zwar nett, bringt aber gewaltige Probleme mit sich, etwa dann, wenn sich die Konstante einmal ändert. Dann muss nämlich auch jede Klasse übersetzt werden, die Bezug auf die Konstante hatte. Falls die abhängigen Klassen nicht neu übersetzt würden, würden sie mit dem alten eincompilierten Wert arbeiten.
Die Lösung ist, die bezugnehmenden Klassen neu zu übersetzen und sich am Besten anzugewöhnen, bei einer Änderung einer Konstante gleich alles neu zu compilieren. Ein anderer Weg transformiert die finale Variable in eine später initialisierte Form:
class Finanz
{
public final static int MWST = new Integer( 16 ).intValue();
}
Die Initialisierung findet in jedem Konstruktor statt und die Konstante mit dem Literal ist erst einmal weg. Der Compiler wird also Finanz.MWST nicht als 16 vorfinden und daher das Literal an den Aufrufstellen nicht einbauen. In der Klassendatei wird der Bezug Finanz.MWST weiterhin zu finden sein, und eine Änderung der Konstante ist für alle Nutzer deutlich.
6.5.3 Typsicherere Konstanten
 
Konstanten sind eine wertvolle Möglichkeit, den Quellcode aussagekräftiger zu machen. Der herkömmliche Weg geht über Ganzzahl-Konstanten:
public final int KONSTANTE1 = 0;
public final int KONSTANTE2 = 1;
public final int KONSTANTE3 = 2;
Dieser Weg bringt den Nachteil mit sich, dass die Konstanten nicht unbedingt von jedem angewendet werden müssen und ein Programmierer eventuell direkt die Zahlen oder Zeichenketten einsetzt. Dieses Problem kommt zum Beispiel auf, wenn ein Font-Objekt für die grafische Oberfläche angelegt werden soll, aber unser Gedächtnis versagt, in welcher Reihenfolge die Parameter zu füllen sind. Ein Fallbeispiel:
Font f = new Font( "Dialog", 12, Font.BOLD );
Leider ist dieses falsch, denn die Argumente für die Zeichensatzgröße und den Schriftstil sind vertauscht. Das Problem ist, dass die Konstanten nur Namen für Werte eines frei zugänglichen Grundtyps sind und nur der Wert an die Funktion übergeben wird. Niemand kann verbieten, dass die Werte direkt eingetragen werden. Das führt dann zu Fehlern, wie im oberen Fall. In diesem ist 12 die Ganzzahl für den Schriftstil, obwohl es dafür nur die Werte 0, 1, 2 geben sollte. Mit Zeichenketten als Wert der Konstanten kommen wir der Lösung auch nicht näher.
Eine gute Möglichkeit, von Ganzzahlen wegzukommen, ist, Objekte einer Klasse als Konstanten einzusetzen. Folgendes bietet sich an:
Listing 6.15
TypsicherGenre.java
public final class TypsicherGenre
{
public static final TypsicherGenre COUNTRY = new TypsicherGenre();
public static final TypsicherGenre TECHNO = new TypsicherGenre();
private int id;
private static int nextId;
private TypsicherGenre() // von aussen lassen sich keine weiteren
{ // Objekte erzeugen
id = nextId++;
}
}
Die Klasse TypsicherGenre definiert die Konstanten als statische Attribute vom Typ TypsicherGenre. Da die Objekte für die Konstanten aber nur einmal vorliegen, lassen sie sich einfach mit ==, wie im folgenden Beispiel mit func() gezeigt, vergleichen.
 Hier klicken, um das Bild zu Vergrößern
Listing 6.16
TypsicherGenre.java
public class TypsichereKonstantenDemo
{
static void func( TypsicherGenre k )
{
if ( k == TypsicherGenre.COUNTRY )
System.out.println( "GEKRINGELT" );
if ( k == TypsicherGenre.TECHNO )
System.out.println( "GESTRICHELT" );
}
public static void main( String args[] )
{
func( TypsicherGenre.TECHNO );
}
}
6.5.4 Aufzählungen und enum in Java 5
 
Wie wir gesehen haben, muss etwas Programmieraufwand getrieben werden, um typsichere Aufzählungen in Java zu realisieren – ein einfaches int reicht nicht aus. Seit Java 5 sind richtige Aufzählungen möglich, denn die Sprache nutzt nun das Schlüsselwort enum, welches früher schon reserviert, aber nicht belegt war. In der Schreibweise für Aufzählungen ersetzt das Schlüsselwort enum das Schlüsselwort class und ist auch ähnlich zu nutzen. (Eine enum-Definition verhält sich wie eine Klassendefinition, nur dass keine Unterklassen und Oberklassen möglich sind. Sonst erlaubt enum auch die Definition von Methoden und Variablen.)
Beispiel Erzeuge Konstanten für die Wochentage.
enum Wochentage {
montag, dienstag, mittwoch, donnerstag, freitag, samstag, Sonntag
}
|
Hinter den Elementen stehen einzelne Objekte, die sich wie alle anderen auch, weiterverarbeiten lassen, das heißt, etwa in eine Datenstruktur einsetzen lassen. Intern erstellt der Compiler normalen Bytecode für eine Klasse Wochentage und mit ihr sieben Objekte. Es gibt »normale« Aufzählungen, die an normale Klassen erinnern und auch »innere« Aufzählungen, die an innere Klassen erinnern. Mit anderen Worten: Wochentage kann auch innerhalb einer anderen Klasse definiert werden.
6.5.5 enum-Konstanten in switch
 
enum-Konstanten sind in switch-Anweisungen möglich, da sie intern über eine Ganzzahl als Identifizierer verfügen, der vom Compiler für die Aufzählung einfach eingesetzt wird.
Wochentage tage = Wochentage.montag;
switch ( tage )
{
case Wochentage.samstag:
case Wochentage.sonntag: System.out.println( "Wochenende. Party!" );
}
Die erzeugten enum-Objekte bekommen standardmäßig eine Reihe von zusätzlichen Eigenschaften. Sie überschreiben sinnvoll toString(), hashCode() und equals() aus Object und implementieren zusätzlich Serializable und Comparable2
. toString() ist so umgesetzt, dass nicht der Ganzzahlwert zu sehen ist, sondern den Name der Konstanten, etwa sonntag. Aufzählungsobjekte können nicht geklont werden. Zusätzlich erbt jedes Aufzählungs-Objekt von der Spezialklasse Enum, die später erklärt wird.
Statische Imports werden auch von enum sinnvoll genutzt
Dazu ein Beispiel. Nehmen wir eine enum-Klasse, die Konstanten definiert, im Paket musik an:
package musik;
public enum Stil { ROCK, POP, TECHNO }
Um jetzt die Konstanten ohne den Aufzählungsnamen zu nutzen, können wir unterschiedliche import-Varianten nehmen, um auf die Konstanten ROCK zuzugreifen.
Import-Anweisung
|
Zugriff
|
import musik.Stil;
|
Stil.ROCK
|
import musik.*;
|
Stil.ROCK
|
import static musik.Stil.*;
|
ROCK
|
Nehmen wir als zweites Beispiel eine innere Aufzählung hinzu:
package musik;
class Musikstil
{
public enum Stil { ROCK, POP, TECHNO; }
}
Import-Anweisung
|
Zugriff
|
import musik.Musikstil;
|
Musikstil.Stil.ROCK
|
import musik.Musikstil.Stil;
|
Stil.ROCK
|
import static musik.Musikstil.Stil.*
|
ROCK
|
1 Ein DIN-A4-Blatt ist 29.7 Zentimeter hoch und 21.0 Zentimeter breit.
2 Die Ordnung der Konstanten ist die Reihenfolge, in der sie geschrieben sind.
|