Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

Java ist auch eine Insel von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 5 (Tiger-Release)
Buch: Java ist auch eine Insel
gp Kapitel 6 Eigene Klassen schreiben
  gp 6.1 Eigene Klassen definieren
    gp 6.1.1 Methodenaufrufe und Nebeneffekte
    gp 6.1.2 Argumentübergabe mit Referenzen
    gp 6.1.3 Die this-Referenz
    gp 6.1.4 Überdeckte Objektvariablen nutzen
  gp 6.2 Assoziationen zwischen Objekten
  gp 6.3 Privatsphäre und Sichtbarkeit
    gp 6.3.1 Wieso nicht freie Methoden und Variablen für alle?
    gp 6.3.2 Privat ist nicht ganz privat. Es kommt darauf an, wer’s sieht
    gp 6.3.3 Zugriffsmethoden für Attribute definieren
    gp 6.3.4 Zusammenfassung zur Sichtbarkeit
    gp 6.3.5 Sichtbarkeit in der UML
  gp 6.4 Statische Methoden und Attribute
    gp 6.4.1 Warum statische Eigenschaften sinnvoll sind
    gp 6.4.2 Statische Eigenschaften mit static
    gp 6.4.3 Statische Eigenschaften als Objekteigenschaften nutzen
    gp 6.4.4 Statische Eigenschaften und Objekteigenschaften
    gp 6.4.5 Statische Variablen zum Datenaustausch
    gp 6.4.6 Warum die Groß- und Kleinschreibung wichtig ist
    gp 6.4.7 Statische Blöcke
  gp 6.5 Konstanten und Aufzählungen
    gp 6.5.1 Konstanten mit dem Schlüsselwort final bei Variablen
    gp 6.5.2 Problem mit finalen Klassenvariablen
    gp 6.5.3 Typsicherere Konstanten
    gp 6.5.4 Aufzählungen und enum in Java 5
    gp 6.5.5 enum-Konstanten in switch
  gp 6.6 Objekte anlegen und zerstören
    gp 6.6.1 Konstruktoren schreiben
    gp 6.6.2 Einen anderen Konstruktor der gleichen Klasse aufrufen
    gp 6.6.3 Initialisierung der Objekt- und Klassenvariablen
    gp 6.6.4 Finale Werte im Konstruktor setzen
    gp 6.6.5 Exemplarinitialisierer (Instanzinitialisierer)
    gp 6.6.6 Zerstörung eines Objekts durch den Müllaufsammler
    gp 6.6.7 Implizit erzeugte String-Objekte
    gp 6.6.8 Private Konstruktoren, Utility-Klassen, Singleton und Fabriken
    gp 6.6.9 Zusammenfassung: Konstruktoren und Methoden
  gp 6.7 Vererbung
    gp 6.7.1 Vererbung in Java
    gp 6.7.2 Einfach- und Mehrfachvererbung
    gp 6.7.3 Gebäude modelliert
    gp 6.7.4 Konstruktoren in der Vererbung
    gp 6.7.5 Sichtbarkeit
    gp 6.7.6 Das Substitutionsprinzip
    gp 6.7.7 Automatische und explizite Typanpassung
    gp 6.7.8 Array-Typen
    gp 6.7.9 Finale Klassen
    gp 6.7.10 Unterklassen prüfen mit dem Operator instanceof
    gp 6.7.11 Methoden überschreiben
    gp 6.7.12 super: Aufrufen einer Methode aus der Oberklasse
    gp 6.7.13 Nicht überschreibbare Funktionen
  gp 6.8 Die oberste aller Klassen: Object
    gp 6.8.1 Klassenobjekte
    gp 6.8.2 Objektidentifikation mit toString()
    gp 6.8.3 Objektgleichheit mit equals() und Identität
    gp 6.8.4 Klonen eines Objekts mit clone()
    gp 6.8.5 Hashcodes
    gp 6.8.6 Aufräumen mit finalize()
    gp 6.8.7 Synchronisation
  gp 6.9 Die Oberklasse gibt Funktionalität vor
    gp 6.9.1 Dynamisches Binden als Beispiel für Polymorphie
    gp 6.9.2 Keine Polymorphie bei privaten, statischen und finalen Methoden
    gp 6.9.3 Polymorphie bei Konstruktoraufrufen
  gp 6.10 Abstrakte Klassen
    gp 6.10.1 Abstrakte Klassen
    gp 6.10.2 Abstrakte Methoden
    gp 6.10.3 Über abstract final
  gp 6.11 Schnittstellen
    gp 6.11.1 Ein Polymorphie-Beispiel mit Schnittstellen
    gp 6.11.2 Die Mehrfachvererbung bei Schnittstellen
    gp 6.11.3 Erweitern von Interfaces – Subinterfaces
    gp 6.11.4 Vererbte Konstanten bei Schnittstellen
    gp 6.11.5 Vordefinierte Methoden einer Schnittstelle
    gp 6.11.6 CharSequence als Beispiel einer Schnittstelle
    gp 6.11.7 Die Schnittstelle Iterable
  gp 6.12 Innere Klassen
    gp 6.12.1 Statische innere Klassen und Schnittstellen
    gp 6.12.2 Mitglieds- oder Elementklassen
    gp 6.12.3 Lokale Klassen
    gp 6.12.4 Anonyme innere Klassen
    gp 6.12.5 Eine Sich-Selbst-Implementierung
    gp 6.12.6 this und Vererbung
    gp 6.12.7 Implementierung einer verketteten Liste
    gp 6.12.8 Funktionszeiger
  gp 6.13 Generische Datentypen
    gp 6.13.1 Einfache Klassenschablonen
    gp 6.13.2 Einfache Methodenschablonen
    gp 6.13.3 Generics und Vererbung
    gp 6.13.4 Einschränken der Typen
    gp 6.13.5 Umsetzen der Generics, Typlöschung und Raw-Types
    gp 6.13.6 Wildcards
  gp 6.14 Die Spezial-Oberklasse Enum
    gp 6.14.1 Methoden auf Enum-Objekten
    gp 6.14.2 enum mit eigenen Konstruktoren und Methoden
  gp 6.15 Gegenseitige Abhängigkeiten von Klassen
  gp 6.16 Veraltete (deprecated) Methoden/Konstruktoren


Galileo Computing

6.14 Die Spezial-Oberklasse Enudowntop

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 );
  }

Galileo Computing

6.14.1 Methoden auf Enum-Objekten  downtop

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 nach seinem Zähler gefragt werden.



class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable

gp  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.
gp  String name()
Liefert den Namen der Konstanten. toString() ruft lediglich name() auf.
gp  static <T extends Enum<T>> T valueOf( Class<T> enumType, String s )
Die Funktion 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.

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.

Da die Enum-Klasse die Schnittstelle Comparable implementiert, gibt es auch die Funktion compareTo().

gp  int compareTo( E o )
Vergleicht anhand der Ordinalzahlen. Nur Vergleiche innerhalb eines Enum-Typs sind erlaubt.

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. 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 und dafür ist der Vergleich mit == richtig. Ein equals() ist denkbar, aber unnötig, denn zum einen implementiert eine konkrete Enum-Klasse gar kein equals(), sondern erbt es von Object und da werden sowie nur Referenzen verglichen und zum anderen lässt sich auch keine Kopie der Enum-Klassen machen, so dass die Identität gefährdet ist. Die Methode clone() ist nur protected und kann nicht aufgerufen werden.


Galileo Computing

6.14.2 enum mit eigenen Konstruktoren und Methoden  toptop

Da eine emum-Klasse die Klassendefinition erweitert, kann sie zusätzlich Attribute und Methoden bekommen.


enum Länder
{
  deutschland( Locale.GERMANY ), england( Locale.UK ), china( Locale.CHINA );

  private Locale land;

  Länder( Locale land )
  {
    this.land = land;
  }
}

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 land. Da switch auf enum erlaubt ist, können wir schreiben:


Länder meinLand = Länder.deutschland;

switch ( meinLand )
{
  case Länder.deutschland: System.out.println( "Aha. Ein Krauti" );
}

Zusätzlich lassen sich auch Methoden definieren, die auf den Enum-Objekten aufgerufen werden können. Betrachten wir dazu eine Klasse, die Monate repräsentiert. Jeder Monat hat eine bestimmte Anzahl von Tagen, die sich mit getDays() erfragen lassen sollte.


enum Month
{
  JAN(31), FEB(28), MAR(31), APR(30), MAY(31), JUN(30),
  JUL(31), AUG(31), SEP(30), OCT(31), NOV(30), DEC(31);

  private int days;

  Month( int days )
  {
    this.days = days;
  }

  int getDays()
  {
    return days;
  }
}

Der Ausdruck Month.MAY.getDays() liefert wie gewünscht 31. Allerdings macht der Februar noch Kummer, denn die Anzahl der Tage ist bei einem Schaltjahr anders. Benötigt wäre in getDays() eine Berechung, abhängig vom Jahr. Im ersten Schritt fügen wir der Aufzählung eine Funktion getDays(int) hinzu, die die Jahreszahl akzeptiert. Die Methode ist erst einmal genauso implementiert wie auch getDays() ohne Parameter. Java erlaubt uns aber, hinter dem Konstantennamen eine Art innere anonyme Klasse zu hängen, die dann Methoden überschreiben kann.


enum Month
{
  JAN(31), FEB(28)
  {
    int getDays( int y )
    {
      return y % 4 == 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;

  Month( int days )
  {
    this.days = days;
  }

  int getDays()
  {
    return days;
  }

  int getDays( int year )
  {
    return days;
  }
}

In der Definition vom Februar überschreien wir das Verhalten von getDays(int), so dass korrekt ausgegeben wird:


System.out.println( Month.FEB.getDays(2000) );    // 29
System.out.println( Month.FEB.getDays(2001) );    // 28

In der Klassendatei steht Ungewöhnliches. Einen wirklichen Konstruktor Month(int) gibt es nicht! Er weicht einem


Month( String s, int i, int j )
{
  super( s, i );
  days = j;
}

und Definitionen der Art


public static final Month JAN = new Month( "JAN", 0, 31 );
public static final Month FEB = new Month_2B_1( "FEB", 1, 28 );

Für den Februar gibt es tatsächlich eine innere anonyme Klasse.





Copyright © Galileo Press GmbH 2004
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de