Java Programmierung
Kap.9:   KLASSENHIERARCHISCHES PROGRAMMIEREN

Kapitel-Index
   9.1   Klassen (class, ggf. generisch) und Objekte 
   9.1.1    Klassen als Referenztypen, Mitglieder 
   9.1.2    Konstruktoren (ggf. generisch), this
   9.1.3    Superklasse, super(), einfache Erbschaft (extends)
   9.1.4    Abstrakte Klassen 
   9.1.5    Rekursive Typen
   9.1.6    Aufzählung (Enumeration)
   9.2   Schnittstellen (interface, ggf. generisch) 
   9.2.1    Schnittstellen als Referenztypen, Mitglieder    
   9.2.2    Superschnittst. (extends), mehrf. Erbsch. (implements)
   9.2.3    Anmerkungstyp (Annotation)
   9.3   Testfragen

  Man verwendet Klassen (9.1), um hierarchische Strukturen mitsamt der darin verfügbaren Methoden zu beschreiben. Klassen können Mitglieder von (Super-) Klassen erben. In Java kann es zu einer Klasse höchstens eine Superklasse geben, d.h. es ist nur "einfache Erbschaft" möglich. "Mehrfache Erbschaft" kann mit Hilfe von Schnittstellen (9.2) simuliert werden.


9.1      Klassen (class, ggf. generisch) und Objekte

  Das Klassen-Konzept stammt aus SIMULA (1966). Eine Klasse ist ein Typ, der neben Datenfeldern, d.h. Klassen-Variablen oder -Konstanten, auch Methoden und innere Klassen oder innere Schnittstellen als Mitglieder enthalten kann. Der Vorteil "klassenhierarchischen Programmierens" liegt in der übersichtlichen Darstellung der Zusammenhänge.

  "Späte Bindung" von Klassen kann erreicht werden durch

   # abstrakte Klassen (9.1.4), d.h.
        Klassen mit Modifizierer abstract bzw.
        mit abstrakter Methoden-Vereinbarung, oder

   # generische Klassen (siehe Beispiel Generic 9.2.2 und
        Beispiel Net 14.1.2 mit generischer Klasse Vector), d.h.
        Klassen- oder Konstruktor- oder Methoden-Vereinbarung
        mit Typ-Parameter-Klausel.

  Mit Klassentypen können namenlose new-Objekte, siehe z.B. SchulKlasse (9.1.1), oder Objekte mit Namen, siehe z.B. Ziffer (9.1.2), erzeugt werden. Nur static Mitglieder von Klassen sind ohne Objekt - Erzeugung zugreifbar, siehe z.B. EinkommenSteuer (9.1.4) mit static-Import des Datenfelds static PrintStream out der Klasse java.lang.System.
                       ,------------------,
            VVVVVVVVVVV| Objekt (Prinzip) |VVVVVVVVVV
            VV         '------------------'        VV
            VV  Ein Objekt ist eine Instanz (Spei- VV
            VV  cher-Bereitstellung) einer Klasse. VV
            VV                                     VV
            VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
  Der Vorteil "objektorientierten Programmierens" liegt in der Werbewirksamkeit des Schlagworts, unter dem man sich "benutzerorientiertes Programmieren" vorstellt, obwohl es sich um "speicheraufwendiges Programmieren" handelt, siehe z.B. TierLaute (9.1.3).


9.1.1    Klassen als Referenz-Typen, Mitglieder 

  Wie schon in Kap. 6 (Reihung, 6.1 ff) ausgeführt, verzichtet man in Java auf explizite Zeiger ('explicite reference considered harmful') und führt statt dessen implizit Zeiger ein, d.h. Referenz-Typen (ReferenceType Syntax.030, 2) für Reihungen (6), Klassen (9.1.1) und Schnittstellen (9.2.1). Ein Zeiger-Zugriff (Referenz) wird realisiert durch eine Konstruktion aus zwei Speichern, wobei der erste die Adresse des zweiten Speichers zum Inhalt hat.
//********************** SchulKlasse.java ************************
//    Klasse mit Lehrer Laempel und Schuelern Max und Moritz     *
//****************************************************************

class Schueler                             // Klassen-Vereinbarung
 {String       name;                        // Mitglied: Datenfeld
  int          note;                        // Mitglied: Datenfeld
  Schueler(String name,int note)                    // Konstruktor
              {this.name=name;this.note=note;}
 }              // `--------------`------------- Datenfeld-Zugriff
class Klasse                               // Klassen-Vereinbarung             
 {String       lehrer;                      // Mitglied: Datenfeld             
  Schueler[]   reihe;                       // Mitglied: Datenfeld             
  void druck()                              // Mitglied: Methode    
   {           System.out.println(lehrer);
    for(Schueler each:reihe)
              {System.out.println(each.name+" Note "+each.note);}
   }
  Klasse(String lehrer,Schueler[] reihe)            // Konstruktor               
              {this.lehrer=lehrer;this.reihe=reihe;druck();}
 }              // `------------------`--------- Datenfeld-Zugriff
class SchulKlasse
 {public static void main(String[] args)         // args ungenutzt
   {Schueler   Max   =new Schueler("Max   ",3),          // Objekt
               Moritz=new Schueler("Moritz",4);          // Objekt
               new Klasse("Laempel",new Schueler[]{Max,Moritz});
            // anonymes Objekt      anonymes Objekt
   }
 }
//****************************************************************
| Output                                       
+-------------            
|Laempel   
|Max    Note 3 
|Moritz Note 4
  Am obigen Beispiel SchulKlasse wird die Vereinbarung von Klassen mit Datenfeldern, Methoden, Konstruktoren und die Instantiierung dieser Klassen durch Vereinbarung von Objekten vorgeführt.

  Eine normale Klassen-Vereinbarung (NormalClassDeclaration) ist nach Syntaxdiagramm Syntax.091 von der Form
091: NormalClassDeclaration       NormaleKlassenVereinbarung           
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
H   ,--------------------,                                       H
H   |  ,---------------, |                                       H
H ->+->| ClassModifier |-'                                       H
H   |  '---------------'                                         H
H   |           ,---------,    ,---------------------,           H
H   '-> class ->|Identifer|--->| TypeParameterClause |-,         H
H               '---------' |  '---------------------' |         H
H                           v    of generic class      |         H
H ,-------------------------+--------------------------'         H
H |               Super-    |               ,-------- , <------, H
H |             ,---------, v               |  ,-------------, | H
H '-> extends ->|ClassType|-+-> implements --->|InterfaceType|-| H
H               '---------' v                  '-------------' | H
H              ,-------------<---------------------------------' H
H              |  C     l     a    s    s     B    o    d    y   H
H              |      ,-------------------------------,          H                            
H              |      | C l a s s B o d y Declaration |          H
H              |      |    ,------------------------, |          H
H              '-> { -+--->| ClassMemberDeclaration |-| ,-> } -> H
H                     | |  '------------------------' | |        H
H                     | |  ,------------------------, | |        H
H                     | |->| ConstructorDeclaration |-| |        H
H                     | |  '------------------------' | |        H
H                     | |-> static ---,    Initializer| |        H
H                     | |             |     ,-------, | |        H
H                     | '------------>----->| Block |-' |        H
H                     |     instance        '-------'   |        H
H                     '---------------------------------'        H
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH

z.B. (siehe SchulKlasse oben)
 class                              // NormalClassDeclaration
  Klasse                               // Class-Identifier  
   {String        lehrer;                 //ClassMemberDeclaration
    Schueler[]    reihe;                  //ClassMemberDeclaration             
    void druck()  {...}                   //ClassMemberDeclaration    
    Klasse(String lehrer,Schueler[] reihe)//ConstructorDeclaration
                  {...}                
   }                                   // ClassBody
  In der Mathematik steht der Begriff Klasse synonym für Menge. Dementsprechend wird in Java eine Klasse vereinbart (NormalClassDeclaration) als Menge von Mitgliedern (ClassMemberDeclaration). Es werden die mathematischen Mengenbildungszeichen { } verwendet.

  Ein Objekt, d.h. die Instanz (Speicherbereitstellung) einer Klasse, wird vereinbart mit dem Klassentyp und initialisiert mit einem Klasseninstanz-Erzeugungsausdruck, der mit einem Allokator (Seicherbereitstellungsoperator) new beginnt und den Konstruktor der Klasse aufruft.

  Ein Klasseninstanz-Erzeugungsausdruck (ClassInstanceCreationExpression) ist nach Syntaxdiagramm 092 von der Form
092: ClassInstanceCreationExpres. KlassenInstanzErzeugungsAusdruck
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
H                       Allocator                                H
H -----------------------> new -,                                H
H  |                            |                                H
H  | ,--------------------------'                                H            
H  | |  ,--------------------------,                             H
H  | |->| NonWildTypeArgumentClause|-,   accessible              H
H  | |  '--------------------------' |   Non-Enum-               H
H  | |   of generic constructor      |  ,--------------------,   H
H  | '------------------------------>-->|ClassOrInterfaceType|-, H
H  |    ,---------,     Allocator       '--------------------' | H
H  '--->| Primary |-> . -> new -,                              | H
H       '---------'             |                              | H
H    ,--------------------------'                              | H            
H    |  ,--------------------------,                           | H                               
H    |->| NonWildTypeArgumentClause|-,                         | H                             
H    |  '--------------------------' |    Constructor-         | H                               
H    |    of generic constructor     |  ,------------,         | H                 
H    '------------------------------>-->| Identifier |-,       | H                 
H                                       '------------' |       | H                 
H    ,-------------------------------------------------'       | H
H    |  ,--------------------------,                           | H
H    |->| NonWildTypeArgumentClause|-,                         | H 
H    |  '--------------------------' |                         | H
H    v    of generic class           |                         | H
H    |---------------------------------------------------------' H
H    |  ,----------------,              ,-----------,            H
H    '->| ArgumentClause |------------->| ClassBody |----------> H
H       '----------------'           |  '-----------'        |   H
H         of constructor             |    of anonymous class |   H
H                                    |                       |   H
H                                    '-----------------------'   H
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH

   z.B. (siehe SchulKlasse oben)
       new                                // Allocator
        Schueler                           // ClassOrInterfaceType
         ("Moritz",4)                       // ArgumentClause 

9.1.2    Konstruktoren (ggf. generisch), this

  Konstruktoren dienen nur zur Klasseninstanz-Erzeugung (9.1.1). Ein Konstruktor ist kein Mitglied seiner Klasse.
                     ,-----------------------,
VVVVVVVVVVVVVVVVVVVVV| Konstruktor (Prinzip) |VVVVVVVVVVVVVVVVVVVV
VV                   '-----------------------'                  VV
VV      Ein Konstruktor einer Klasse ähnelt einer Methode,      VV
VV     auch hinsichtlich Signatur und Überladen (7.3), aber     VV
VV                                                              VV
VV # ein Konstruktor hat den selben Bezeichner wie die Klasse,  VV
VV # ein Konstruktor hat keinen Ergebnistyp (auch nicht void).  VV
VV                                                              VV
VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV

//************************* Ziffer.java **************************
//        Deutung chinesischer Ziffer-Bilder, Zhou, 1.Jtv.       *
//        Deutung deutscher Zahl-Worte, IndoEuropa, 2.Jtv.       *
//****************************************************************
class Zif
 {final               String deutung;               // Datenfeld
  void druck()       {System.out.println(deutung);} // Methode
  Zif(String deutung){this.deutung=deutung;druck();}// Konstruktor
 }
class Ziffer 
 {public static void main(String[] args)         // args ungenutzt
  {Zif
   z1=new Zif(  "    \r\n ---1 Horizt Yang\r\n      EIN weSen  "),
   z2=new Zif(  " --,\r\n  / 2 Him+Erd Yin\r\n `--  ZWEIgwesen "),
   z3=new Zif(  " -, \r\n -+-3 Trieb links\r\n -'   TREIbwesen "),
   z4=new Zif(  ",--,\r\n||||4 4Eck+2Teilg\r\n`||'  FInger quER"),
   z5=new Zif(  "-,  \r\n +-,5 Hand links \r\n-' |  FINgerWesen"),
   z6=new Zif(" /\\ \r\n/||\\6 3Eck+2Teilg\r\n ||   SaEGweSen  "),
   z7=new Zif(  " -, \r\n -+-7 RegenDrache\r\n  |_  SIEBwesEN  "),
   z8=new Zif(  "    \r\n || 8 2Teilung   \r\n ||   AuCH Tswei "),
   z9=new Zif(  " ,-,\r\n,+,|9 3*3Peitsche\r\n³³³|  NEU Neu    ");
  }
}
//****************************************************************
  
| Output            | Output (Fortsetzung)  | Output (Fortsetzung)
+-----------------  +---------------------  +---------------------
|                   |,--,                   | -,
| ---1 Horizt Yang  |||||4 4Eck+2Teilg      | -+-7 RegenDrache
|      EIN weSen    |`||'  FInger quER      |  |_  SIEBwesEN
| --,               |-,                     |
|  / 2 Him+Erd Yin  | +-,5 Hand links       | || 8 2Teilung   
| `--  ZWEIgwesen   |-' |  FINgerWesen      | ||   AuCH Tswei
| -,                | /\                    | ,-,
| -+-3 Trieb links  |/||\6 3Eck+2Teilg      |,+,|9 3*3Peitsche        
| -'   TREIbwesen   | ||   SaEGweSen        |³³³|  NEU Neu
  Beim obigen Programm Ziffer verbindet der Autor die "Pflicht", ein einfaches Konstruktor-Beispiel vorzuführen, mit der "Kür", die alten chinesischen Ziffer-Bilder aus der Zhou-Dynastie und die entsprechenden deutschen aus dem Indoeuropäischen stammenden ZahlWorte zu deuten, siehe
   http://www.harry-feldmann.net/Urbild/Urbild.html
  In der obigen Klasse Zif besetzt der Konstruktor Zif(String) das Datenfeld deutung mit einem Wert und ruft dann die Methode druck() auf. Es kann Klassen ohne Konstruktor geben, dafür existiert ein Default-Konstruktor mit Argument-Klausel ohne Argumente, und es können Datenfelder durch den Konstruktor nicht mit Werten besetzt sein, dann werden die Datenfelder mit Vorgabe-Nullwerten (DefaultNullValue Syntax.038) besetzt. Es kann auch Klassen mit mehreren hinsichtlich ihrer Signatur verschiedenen Konstruktoren geben.

  Ein Datenfeld-Zugriff (FieldAccess) ist nach Syntaxdiagramm 075 von der Form
075: FieldAccess                  DatenfeldZugriff
          HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
          H     ,---------,                            H
          H --->| Primary |-> . ---,                   H
          H  |  '---------'        |                   H
          H  |   e.g. this         |                   H
          H  |                     |                   H
          H  |    Class-           |                   H
          H  |  ,------,           |                   H
          H  |->| Name |----> . -, |                   H
          H  v  '------'         | |    Field-         H
          H  |-------------------' |  ,------------,   H
          H  '-> super -----> . ----->| Identifier |-> H
          H                           '------------'   H
          HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH

             z.B. (siehe Klasse Zif in Ziffer oben)
                       this.deutung
  Der formale Parameter String deutung des Konstruktors hat den gleichen Bezeichner wie das Datenfeld String deutung. Für die eindeutige Bestimmung der linken Seite der Zuweisung
                       this.deutung=deutung;
wird der Datenfeld-Zugriff this.deutung verwendet. Damit erübrigt sich eine Umbenennung des formalen Parameters und der direkte Bezug zum Datenfeld bleibt erkennbar. Im obigen Beispiel Ziffer wird bei den Vereinbarungen der Objekte z1,...,z9 vom Typ Zif durch Aufruf des Konstruktors Zif(String deutung) das Datenfeld String deutung mit dem jeweils als Konstruktor-Argument genannten String-Literal besetzt.

  Konstruktor-Vereinbarer (Constructor-Declarator Syntax.087) können auch eine Typ-Parameter-Klausel besitzen, d.h. einen generischen Konstruktor vereinbaren (siehe Beispiel Generic 9.2.2).

9.1.3    Superklasse, super(), einfache Erbschaft (extends)

  Im folgenden Beispiel TierLaute werden eine Superklasse Tier und zwei Subklassen Hahn, Hund vereinbart, d.h. eine Hierarchie:
                            Tier
                              |
                           ,-------...-,
                           |     |     |
                         Hahn  Hund weitere
                                    möglich

//*********************** TierLaute.java *************************
//    Kinder-Worte fuer Tierlaute in 5 verschiedenen Sprachen.   *
// Hierarchie aus 3 Klassen. Erzeugung von 30 Klassen-Instanzen. *
//****************************************************************
abstract class Tier                              // Superklasse 
 {public static String[] sprachen=                  // Datenfeld
   {" frnz."   ," dtsh."   ," russ."   ," pers."   ," chin."   };
  protected Tier(int i)                             // Konstruktor
   {System.out.print(sprachen[i]);}                 
  protected void gibLaut(String art,String laut)    // Methode
   {System.out.print(art+": "+laut);} 
 }

class Hahn extends Tier                          // Subklasse
 {private String[] laute=                           // Datenfeld
   {"KOkRIKO  ","KIKERIKI ","KUKAREKU ","kUkULIkU ","KOKELOcO "};
  Hahn(int i) {super(i);gibLaut("Hahn",laute[i]);}  // Konstruktor
 }           //     `----------------- ExpliziterKonstruktorAufruf
class Hund extends Tier                          // Subklasse
 {private String[] laute=                           // Datenfeld
   {"OUAH OUAH","WAU WAU  ","GAV GAV  ","HAp HAp  ","WAn WAn  "};
  Hund(int i) {super(i);gibLaut("Hund",laute[i]);}  // Konstruktor
 }           //     `----------------- ExpliziterKonstruktorAufruf

class TierLaute 
 {public static void main(String[] args)         // args ungenutzt
   {for(int i=0;i<Tier.sprachen.length;i++)
     {new Hahn(i);new Hund(i);System.out.println();}
    //   `-----------`----------- KlassenInstanzErzeugungsAusdruck
   }                                                
 }
//****************************************************************
                                         Hierarchie (30 Instanzen)
| Output                                         Object (10)
+-----------------------------------------           |     
| frnz.Hahn: KOkRIKO  frnz.Hund: OUAH OUAH         Tier (10)
| dtsh.Hahn: KIKERIKI dtsh.Hund: WAU WAU             |          
| russ.Hahn: KUKAREKU russ.Hund: GAV GAV        ,---------,   
| pers.Hahn: kUkULIku pers.Hund: HAp HAp        |         |
| chin.Hahn: KOKELOcO chin.Hund: WAn WAn      Hahn (5)  Hund (5)
  Mit "Hahn extends Tier" und "Hund extends Tier" wird in den Klassen-Vereinbarungen (NormalClassDeclaration Syntax.091, 9.1.1) von Hahn und Huhn festgelegt, dass sie Subklassen der Superklasse Tier sind. Da in einer Klassen-Vereinbarung höchstens ein extends mit höchstens einem nachfolgenden (Super-) Klassen-Bezeichner vorkommen kann, ist nur "einfache Erbschaft" möglich.

  Zugriffsrechte werden mit Zugriff-Modifizierern (AccessModifier, Modifier Syntax.070) geregelt. Im obigen Beispiel TierLaute ist das Datenfeld public static String[] sprachen qualifiziert zugreifbar in jedem importierenden Paket sowie direkt zugreifbar in der aktuellen Klasse Tier und in den Subklassen Hahn und Hund. Die Methode protected void gibLaut(...) ist qualifiziert zugreifbar im aktuellen Paket sowie direkt zugreifbar in der aktuellen Klasse Tier und in den Subklassen Hahn und Hund. Das Datenfeld private String[] laute ist nur zugreifbar in der eigenen Klasse Hahn bzw. Hund. Der Modifizierer abstract der Klasse Tier kann fortgelassen werden,da die Klasse Tier keine abstrakte Methode besitzt (9.1.4). Er zeigt nur an, dass keine Objekte dieser Klasse gebildet werden.

  Eine Konstruktor-Vereinbarung (ConstructorDeclaration) ist nach Syntaxdiagramm 089 von der Form
089: ConstructorDeclaration       KonstruktorVereinbarung
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
H   ,--------------------------,                                 H
H   |  ,---------------------, |                                 H
H ->+->| ConstructorModifier |-'                                 H
H   |  '---------------------'                                   H
H   |  ,-----------------------,    ,--------,                   H
H   '->| ConstructorDeclarator |--->| Throws |-,                 H
H      '-----------------------' v  '--------' |                 H
H   ,------------------------------------------'                 H
H   | C   o   n   s   t   r   u   c   t   o   r   B   o   d   y  H
H   |         ,--------------,   ,-------------------,           H
H   |         | Explicite-   |   |  ,--------------, |           H
H   '-> { --->| Constructor- |---+->|BlockStatement|-',---> } -> H
H          |  | Invocation   | ^ |  '--------------'  |          H
H          |  '--------------' | '--------------------'          H
H          '-------------------'                                 H
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
z.B. (siehe TierLaute oben)
   Hahn(int i)             // ConstructorDeclaration
    {                         // ConstructorBody
     super(i);                   // ExpliciteConstructorInvocation
     gibLaut("Hahn",laute[i]);   // BlockStatement
    }
  Mit dem expliziten Konstruktor-Aufruf (ExpliciteConstructorInvocation, Syntax.088) am Anfang der Konstruktor-Vereinbarungen von Hahn und Hund
                         super(i);
wird der Konstruktor der Superklasse Tier aufgerufen.

  Dort, in der Tier-Hierarchie, darf "super(i);" nicht ersetzt werden durch "new Tier(i);", auch nicht nach Weglassen des Modifizierers abstract der Klasse Tier.

  Um weitere Subklassen, z.B. Pferd, der Superklasse Tier hinzuzufügen, braucht die Superklasse Tier nicht verändert zu werden. Klassen-hierarchisches Programmieren unterstützt also das vom strukturierten Programmieren her bekannte Konzept der "schrittweisen Verfeinerung".


  Das obige Beispiel TierLaute zeigt, dass für die kleine Hierarchie aus 3 Klassen Hahn, Hund und Tier bereits 30 Instanzen angelegt werden: Mit den Klassen Hahn und Hund werden jeweils 5 namenlose new-Objekte angelegt, insgesamt 10 Instanzen. Für jede der 10 Instanzen von Hahn und Hund wird jeweils eine neue Instanz ihrer Superklasse Tier angelegt und außerdem für jede der 10 Instanzen von Tier eine Instanz der Superklasse Object.

  Die Klasse java.lang.Object ist direkt oder indirekt Superklasse jeder Klasse. Sie enthält z.B. die Methode toString() für String -Darstellung (2.6) eines Objekts sowie die Methoden wait() und notifyAll() für synchronisiertes Multithreading (12.2).


  Probleme mit einer größeren Anzahl von Objekten, z.B. Auftragsabwicklung mit Kunden und Artikeln, sollten nicht mit "objektorientiertem Programmieren" gelöst werden, sondern mit relationalen Datenbanken, z.B. MySQL, für die es Konnektoren zu Java gibt (15).



9.1.4    Abstrakte Klassen

  Eine Klasse kann mit einem Modifizierer abstract vereinbart werden, wenn kein Objekt dieser Klasse gebildet werden soll, vgl. Tier in TierLaute (9.1.3). Eine Klasse muss mit einem Modifizierer abstract vereinbart werden, wenn sie abstrakte Mitglieder (ClassMemberDeclaration, Syntax.090) enthält, vgl. Tarif im folgenden Bei- spiel EinkommenSteuer.

  Für Subklassen abstrakter Klasse oder - falls diese wieder abstrakt sind - für Subklassen dieser Subklassen können dann Objekte gebildet werden.

  Eine abstrakte (unvollständige) Methode, d.h. eine Methode mit einem Modifizierer abstract, enthält in ihrer Vereinbarung (MethodDeclaration Syntax.083, 7.1) statt des Methoden-Rumpfs nur ein Semikolon. Sie dient als Platzhalter für eine Signatur-gleiche vollständig mit Methoden-Rumpf in einer Subklasse vereinbarten Methode.

  Es folgt das Beispiel EinkommenSteuer mit abstrakter Klasse Tarif und darin vereinbarter abstrakter Methode hebSteuer(int).
  Zur Superklasse Tarif werden zwei Subklassen Grund und Split vereinbart, d.h. die folgende Klassenhierarchie:
                               Tarif
                                 |
                              ,-----,
                              |     |
                            Grund Split

  Die Steuererhebung mit Abfrage und Ausdruck ist in Tarif als abstrakte Methode hebSteuer(int) vereinbart. Sie wird in Grund und in Split überschrieben.

  Die eigentliche Steuerberechnung ist in Tarif als nicht-abstrakte Methode setzTarif(int) vereinbart. Sie gilt sowohl für Grund als auch für Split.
//********************* EinkommenSteuer.java *********************
//                Grund- und Splitting-Tarif 2010.               *
//        Command: java EinkommenSteuer Einkommen [split]        *
//****************************************************************
abstract class Tarif 
 {protected int setzTarif(int x)
   {if     (x<=8004)  {return 0;}
    else if(x<=13469) {double y=0.0001*(x-8004);
                       return(int)((912.17*y+1400)*y);}
    else if(x<=52881) {double z=0.0001*(x-13469);
                       return(int)((228.74*z+2397)*z+1038);}
    else if(x<=250730){return(int)(0.42*x-8172);}
    else              {return(int)(0.45*x-15694);}
   }
  abstract int hebSteuer(int einkommen);
 }
class Grund extends Tarif                         
 {int hebSteuer(int einkommen)
                      {return   setzTarif(einkommen  );}
 }
class Split extends Tarif                         
 {int hebSteuer(int einkommen)
                      {return 2*setzTarif(einkommen/2);}
 }
class EinkommenSteuer          
 {public static void main(String[] args) //args[0],args[1] genutzt
   {int                einkommen=(int)Double.parseDouble(args[0]);
    if                (args.length==1)
    {System.out.printf("%7d",new Grund().hebSteuer(einkommen));}
    else
    {System.out.printf("%7d",new Split().hebSteuer(einkommen));}
   }
 }
//****************************************************************
                                                     Hierarchie
| Command                               | Output        Tarif
+------------------------------------   +-------          |
|java EinkommenSteuer 100000.00         |  33828       ,-----,
|java EinkommenSteuer  10000.00         |    315       |     |
|java EinkommenSteuer 110000.00 split   |  29856     Grund Split
  Verdient der eine Ehepartner pro Jahr 100000 EUR und der andere 10000 EUR, so ist es für sie günstiger, sich nach Splitting-Tarif versteuern zu lassen, d.h. ihr gemeinsames Einkommen von 110000 EUR aufzuteilen (spliting) und zweimal 55000 EUR zu versteuern, als sich einzeln nach Grund-Tarif versteuern zu lassen.









9.1.5    Rekursive Typen


  Ein rekursiver Typ ist ein Klassentyp, der in seiner Klassen -Vereinbarung (9.1.1) Mitglieder vom selben Klassentyp vereinbart. Unendliche Vereinbarungs-Ketten können dadurch vermieden, dass nach endlich vielen Schritten die Mitglieder vom selben Klassentyp mit dem NullZeiger null besetzt werden.

  Das folgende Programm ListUpdate ist sowohl ein Beispiel für rekursive Typen,
 z.B. class List                                          
             {final String item;      // ,----,                   
              List suc;               // |item|->suc     Rekursion
              ... }                   // '----'
als auch für rekursive Methoden (7.3),
 z.B. void  append(final String s)    // String s an List anhaegen 
             {if(suc!=null)        {suc.append(s);}   // Rekursion 
              else               {suc=new List(s);}
             }
 z.B. void  removeNextEqual(String s) // naechsten String s  
             {if(suc!=null)           // aus List entfernen 
              if(s.equals(suc.item)) {suc=suc.suc;}
              else        {suc.removeNextEqual(s);}   // Rekursion
             }
 z.B. void  traverse()                // alle folgenden String 
             {out.print(item+"->");   // item von List drucken
              if(suc!=null)        {suc.traverse();} // Rekursion
             }
  Die Mitglieder item und suc einer neu erzeugten List-Instanz werden zunächst mit dem Vorgabe-Nullwert (DefaultNullValue Syntax .038) null besetzt. Die Anweisung suc=new List(s); besetzt den Nachfolger suc mit s. Die Anweisung suc=suc.suc; entfernt den Nachfolger suc durch Umsetzen auf den Nachfolger suc.suc des Nachfolgers suc.
  ConsoleInput ist die in 8.2 beschriebene Klasse mit der Methode get() zur Keyboard-Eingabe eines String-Werts über die DOS-Console. ConsoleInput.java muss im aktuellen Verzeichnis stehen.
//*********************** ListUpdate.java ************************
//                      Listenverarbeitung                       *
//      Anhaengen und Entfernen von  Gliedern  aus einer Liste.  *
//      ConsoleInput.java muss im aktuellen Verzeichnis stehen.  *
//                                                               *
//****************************************************************
//            java.lang.                                  Object
import static java.lang.System.out;                     // `System

class List                                          // ,----,  
 {final  String item;                               // |item|->suc 
  List          suc;                                // '----' 
  List ( String item)                 {this.item=item;} 

  void append  (final String s) 
   {if         (suc!=null)            {suc.append(s);}
    else                              {suc=new List(s);}
   }
  void removeNextEqual(String s)
   {if         (suc!=null)
     if        (s.equals(suc.item))   {suc=suc.suc;}
     else                             {suc.removeNextEqual(s);}
   }
  void traverse()
   {out.print  (item+"->");
    if         (suc!=null)            {suc.traverse();}
   }
 }

class ListUpdate extends ConsoleInput
 {public static void main(String[] args)         // args ungenutzt
   {List        first=null;
    String      s;
    for(;;)
     {out.print("\r\nString(+app,-rem,#break): ");
                s=get();
      if       (s.startsWith("#"))    {break;}
      if       (s.startsWith("+"))
       {        s=s.substring(1);
        if     (first==null)          {first=new List(s);}
        else                          {first.append(s);}
       }
      if(s.startsWith("-"))
       {        s=s.substring(1);
        if     (first!=null)
         if    (s.equals(first.item)) {first=first.suc;}
         else                         {first.removeNextEqual(s);}
       }
      if       (first!=null)          {first.traverse();}
     }
   }
 }
//****************************************************************
| Output                           | Input
+----------------------------------+-------
|                                  |      
|String(+app,-rem,#break):         |+Hansi
|Hansi->                           |
|String(+app,-rem,#break):         |+Lutz
|Harry->Lutz->                     |
|String(+app,-rem,#break):         |+Rosi
|Hansi->Lutz->Rosi->               |
|String(+app,-rem,#break):         |+Harry
|Hansi->Lutz->Rosi->Harry->        |
|String(+app,-rem,#break):         |-Hansi
|Lutz->Rosi->Harry->               |
|String(+app,-rem,#break):         |#

9.1.6    Aufzählung (Enumeration)

  Eine Aufzählungsvereinbarung (EnumDeclaration) ist nach Syntaxdiagramm 094 von der Form
094: EnumDeclaration              AufzählungsVereinbarung           
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
H   ,--------------------,                                       H
H   |  ,---------------, |                                       H
H ->+->| ClassModifier |-'                                       H
H   |  '---------------'                                         H
H   |                                                            H
H   |            Enum-                       ,------- , <------, H
H   |          ,-----------,                 | ,-------------, | H
H   '-> enum ->| Identifer |---> implements -->|InterfaceType|-| H
H              '-----------' |                 '-------------' | H 
H                            v                                 | H
H ,---------------------------<--------------------------------' H
H |  E       n       u       m       B       o       d       y   H
H |                                     ,-----------,            H
H |       ,---- , <----, ,-> , -,     ,-|ClassBody- |-,          H
H |       | ,--------, | |      |     | |Declaration| |          H                                            
H |       | | Enum-  | | |      |     v '-----------' |          H                        
H '-> { ->->|Constant|-->+----->+-> ; ----------------->--> } -> H
H       |   '--------'   ^      |                       ^        H
H       |                |      |                       |        H
H       '----------------'      '-----------------------'        H
H                                                                H
H A  ClassModifier  for an Enum-  Ein  KlassenModifizierer einer H
H Declaration is implicitely fi-  AufzählungsVereinbarung    ist H
H nal (do'nt write) and must not  implizit final  (nicht schrei- H
H be abstract.                    ben)  und  darf nicht abstract H 
H                                 sein.                          H 
H An EnumType must not be expli-  Ein  AufzählungsTyp darf nicht H
H citely instantiated.            explizit instantiiert werden.  H
H                                                                H
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
            z.B. (siehe Tage unten)
                  enum Tag{MO,DI,MI,DO,FR,SA,SO}

  Im folgenden Programm Tage wird eine enum-Klasse enum Tag mit num-Konstanten MO,DI,MI,DO,FR,SA,SO vereinbart.
/*************************** Tage.java ***************************
/    enum-Klasse Tag mit enum-Konstanten MO,DI,MI,DO,FR,SA,SO    *
/*****************************************************************

/            java.lang.                              Object
/            java.lang.                               ||`Enum<E>
mport static java.util.EnumSet.range;              // |`EnumSet<E>
mport static java.lang.System .out;                // `System

num Tag        {MO,DI,MI,DO,FR,SA,SO}               // enum-Klasse

lass Tage 
{public static void main(String[] args)          // args ungenutzt
  {out.print   ("Wochentage  ");
   for(Tag      each:Tag.values())             // Methode values()
    {out.print (each+" ");
    }
   out.println ();                          
   out.println ("blauer Tag  "
               +Tag.valueOf("MO"));     // Methode valueOf(String)
   out.print   ("Arbeitstage ");
   for(Tag      each:range(Tag.MO,Tag.FR))   // Methode range(E,E)
    {out.print (each+(each==Tag.MO?"=blau ":" "));  // Operator ==
    }
   out.println ();                          
   out.print   ("S=Feiertage ");
   for(Tag      each:Tag.values())             // Methode values()
    {out.print ((each.name().charAt(0)=='S'?each+" ":"")); // Enum
    }                                               
   out.println ();                          
  }
}

//****************************************************************

| Output                                                 
+--------------------------------     
|Wochentage  MO DI MI DO FR SA SO
|blauer Tag  MO                 
|Arbeitstage MO=blau DI MI DO FR
|S=Feiertage SA SO
  Java unterteilt Klassen syntaktisch (Syntax.101) in normale Klassen (NormalClassDeclaration Syntax.091, 9.1.1) und enum-Klassen (EnumDeclaration, siehe oben, Syntax.094).

  Im Unterschied zu normalen Klassen werden enum-Klassen bei ihrer Vereinbarung implizit zu enum-Objekten instantiiert und können nicht explizit als benannte Objekte oder namenlose new-Objekte instantiiert werden.

  Zu jeder enum-Klasse E sind u.a. definiert (siehe Beispiel Tage oben):
  # der Operator == 
      zur Prüfung der Gleichheit von enum-Konstanten,
  # die Methode public static E valueOf(String name) der Klasse E
      zur Bestimmung der enum-Konstante von E mit Namen name, 
  # die Methode public static E[] values() der Klasse E
      zur Erzeugung der Reihung der enum-Konstanten von E,
  # die Superklasse Enum<E> der enum-Klasse E im Paket java.lang
      (siehe Java Dokumentation), u.a. mit der Methode name()
      zur Konvertierung einer enum-Konstanten in einen String und
  # die Superklasse EnumSet<E> der enum-Klasse E 
      im Paket java.util (siehe Java Dokumentation),
      u.a. mit der Methode range(E from,E to)
      zur Erzeugung einer enum-Klasse mit den
      enum-Konstanten im angegebenen Bereich from ... to von E .
  Das nachfolgende Programm Muenze zeigt, dass enum-Klassen-Vereinbarungen auch enum-Konstanten mit Argument-Klausel enthalten können und nachfolgend Datenfelder, Methoden und einen Konstruktor, in den das Argument der enum-Konstante als aktueller Parameter eingesetzt wird.
//************************* Muenze.java **************************
//                      enum-Klasse Muenze                       *
//    mit enum-Konstanten und zugehoerigen Argument-Klauseln,    *
//           mit Datenfeld, Methoden und Konstruktor             *
//****************************************************************
enum Farbe {AUSSENWEISS_INNENGELB,AUSSENGELB_INNENWEISS,GELB,ROT}
enum Muenze
 {                      EURO_2(2.00),EURO_1(1.00),// Konstanten 
                        CENT50(0.50),CENT20(0.20),CENT10(0.10),
                        CENT05(0.05),CENT02(0.02),CENT01(0.01);
  private final double  wert;                     // Datenfeld
  Muenze(double wert)  {this.wert=wert;}          // Konstruktor
  double wert()        {return wert;}             // Methode
  Farbe farbe(Muenze m)                           // Methode
   {switch(m)
     {case EURO_2:      return Farbe.AUSSENWEISS_INNENGELB;
      case EURO_1:      return Farbe.AUSSENGELB_INNENWEISS;
      case CENT50:
      case CENT20:
      case CENT10:      return Farbe.GELB;
      default:          return Farbe.ROT;
     }
   }
  public static void main(String[] args)          // Methode
   {for(Muenze          each:Muenze.values())     //args ungenutzt
     {System.out.printf("%1$6s: %2$4.2f EUR %3$s\r\n",
                        each.name(),each.wert(),each.farbe(each));
 } } }
//****************************************************************
| Output
+--------------------------------------
|EURO_2: 2,00 EUR AUSSENWEISS_INNENGELB
|EURO_1: 1,00 EUR AUSSENGELB_INNENWEISS
|CENT50: 0,50 EUR GELB
|CENT20: 0,20 EUR GELB
|CENT10: 0,10 EUR GELB
|CENT05: 0,05 EUR ROT
|CENT02: 0,02 EUR ROT
|CENT01: 0,01 EUR ROT





9.2      Schnittstellen (interface, ggf. generisch)


  Das Schnittstellenkonzept (InterfaceType) von Java unterstützt den Entwurf von größeren, in Teamarbeit zu bewältigenden Programmiervorhaben. Die Schnittstellen-Hierarchie verwirklicht wie die Klassen-Hierarchie (siehe Tierlaute, 9.1.3) das vom strukturierten Programmieren her bekannte Konzept der "schrittweisen Verfeinerung".

  Schnittstellen-Vereinbarungen (9.2.2) können auch Typ-Parameter enthalten, d.h. generische Schnittstellen vereinbaren, siehe Beispiel Generic 9.2.2.




9.2.1    Schnittstellen als Referenztypen, Mitglieder             

  Wie schon in Kap. 6 (Reihung, 6.1 ff) ausgeführt, verzichtet man in Java auf explizite Zeiger ('explicite reference considered harmful') und führt statt dessen implizit Zeiger, d.h. Referenz -Typen (ReferenceType Syntax.030,2) ein für Reihungen (6), Klassen (9.1.1) und Schnittstellen (9.2.1). Ein Zeiger-Zugriff (Referenz) wird realisiert durch eine Konstruktion aus zwei Speichern, wobei der erste die Adresse des zweiten Speichers zum Inhalt hat.

  Ähnlich wie Klassen (ClassMemberDeclaration Syntax.090) können auch Schnittstellen (InterfaceMemberDeclaration Syntax.095) Datenfelder, Methoden und innere Klasse oder innere Schnittstellen als Mitglieder haben, aber im Unterschied zu Klassen gibt es bei Schnittstellen nur implizit final vereinbarte Datenfelder und nur implizit abstract vereinbarte Methoden.



9.2.2    Superschnittst. (extends), mehrf. Erbsch. (implements)

  Eine normale Schnittstellen-Vereinbarung (NormalInterfaceDecla- ration) ist nach Syntaxdiagramm 096 von der Form
096: NormalInterfaceDeclaration   NormaleSchnittst.Vereinbarung
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
H                                                                H
H   ,------------------------,                                   H
H   |  ,-------------------, |                                   H
H ->+->| InterfaceModifier |-'                                   H
H   |  '-------------------'                                     H
H   |                 Interface-                                 H
H   |               ,-----------,     ,---------------------,    H
H   '-> interface ->| Identifer |---->| TypeParameterClause |-,  H
H                   '-----------' |   '---------------------' |  H
H                                 |     of generic interface  |  H
H                                 v                           |  H
H   ,------------------------------<--------------------------'  H
H   |            ,--------- , <-------,                          H
H   |            |  ,---------------, |                          H
H   |-> extends --->| InterfaceType |-|                          H
H   |               '---------------' |                          H
H   v                                 |                          H
H   |<--------------------------------'                          H
H   |       I   n   t   e   r   f   a   c   e   B   o   d   y    H
H   |            ,----------------------------------,            H                           
H   |            |  ,----------------------------,  |            H
H   '-------> { -+->| InterfaceMemberDeclaration |--' ,-> } ->   H
H                |  '----------------------------'    |          H
H                '------------------------------------'          H
H                                                                H
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH

  z.B. (siehe RingForm unten)
       interface Left  extends Upper {String TEXT="Left"; }
  Man überzeuge sich an Hand der Syntaxdiagramme für normale Klassen-Vereinbarung (Syntax.091, 9.1.1) und normale Schnittstellen - Vereinbarung (Syntax.096, siehe oben) mit K = Klasse und S=Schnittstelle, dass für
     # Klassen        höchstens  extends K implements S1,..,Sn  ,
     # Schnittstellen höchstens  extends S1,...,Sn
zulässig ist.

  Dass bei Klassen auch die Reihenfolge "extends vor implements" verbindlich vorgeschrieben wird, ist eine unnötige Überreglementierung.

  Eine Klasse kann ihren Bestand erweitern (extends K), d.h. Mitglieder ihrer Superklasse, z.B. vollständig implementierte Methoden, annehmen, und ist in der Lage, Schnittstellen zu implementieren (implements S1,...,Sn),

  Eine Schnittstelle kann keine Mitglieder von Klassen, z.B. vollständig implementierte Methoden, annehmen und kann nicht implementieren, sondern kann nur ihren Bestand erweitern (extends S1,... Sn) durch Annahme von Mitgliedern ihrer Superschnittstellen, z.B. unvollständig implementierten (abstract) Methoden.
  Für Klassen ist nur "einfache Erbschaft" zugelassen (9.1.3), d.h. zu einer Klasse kann es höchstens eine Superklasse geben. Mit der Einführung von Schnittstellen wird auch "mehrfache Erbschaft" (multiple inheritance) ermöglicht, d.h. zu einer Schnittstelle kann es mehrere Superschnittstellen geben (extends S1,..., Sn) und eine Klasse kann mehrere Schnittstellen implementieren (implements S1,...,Sn).


  Im nachfolgenden Programm hat die Klasse MehrfachErbe eine Superklasse (extends) Tante und eine Schnittstelle (implements) Onkel, d.h. man könnte hier von "mehrfacher Erbschaft" sprechen. Von seiner Tante erbt der MehrfachErbe die vollständig implementierte Methode nimmEUR(,), d.h. er bekommt 300000,00 EUR, ohne sich anzustrengen. Von seinem Onkel erbt der MehrfachErbe die unvollständig implementierte Methode fassSelbstvertrauen(), die er selbst implementieren muss (implements), um Selbstvertrauen zu gewinnen.
//*********************** MehrfachErbe.java **********************
// Mehrfach-Erbe erbt (extends)    von class    Tante bares Geld *
// Mehrfach-Erbe erbt (implements) vom interface Onkel guten Rat *
//****************************************************************
//     java.lang  .                              O b j e c t
import java.awt   .*;                  // Component'`Font`Graphics
import java.applet.Applet;             //  Applet'
                                              
class Tante extends Applet                    
 {double                EUR=300000.00;                       
  void nimmEUR(Graphics g,int size)        
   {g.drawString  
         (String.format(" Tante:\"nimm %9.2f EUR!\"",EUR),0,size);
   }
 }

interface Onkel                          
 {String                Selbstvertrauen="Selbstvertrauen";     
  void fassSelbstvertrauen(Graphics g);                // abstract 
 }
                                                   // Tante Onkel
public class MehrfachErbe extends    Tante         //   |     |
                          implements Onkel         //   '-----'
 {                                                 //      |  
  int                   size;                      // MehrfachErbe
  public void fassSelbstvertrauen(Graphics g)               
   {g.drawString(" Onkel:\"fass "+Selbstvertrauen+"!\"",0,2*size);
   }
  public void paint(Graphics g)
   {nimmEUR            (g,size);
    fassSelbstvertrauen(g);}
  public void start()                          // overrides Applet
   {                    size=getHeight()/3;
    setFont            (new Font("SansSerif",Font.BOLD,size));
   }                                              // calls paint()
 }                    
//****************************************************************

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"   >
<!--***************** MehrfachErbe.html ***********************-->
    <meta http-equiv="content-type"
             content="text/html;charset=ISO-8859-15"             >
    <title>           MehrfachErbe                        </title>
    <applet     code="MehrfachErbe.class" width="636"height="126">
                      MehrfachErbe
    </applet>
<!--***************** width=Hochformat height=3*size **********-->

Fig.9.2.2.gif
Fig. 9.2.2: MehrfachErbe Darstellung






  Schnittstellen sind implizit abstract, ihre Datenfelder implizit public static final, ihre Methoden implizit abstract. Die Einschränkung auf abstrakte Methoden unterscheidet Schnittstellen wesentlich von Klassen. Die Einschränkung auf konstante Datenfelder (final) ist kein wesentlicher Unterschied zu Klassen. Eine Schnittstellen könnte mit einem Datenfeld String Faust komplett Goethes "Faust" erfassen. Daher können Schnittstellen auch als "Datei-Ersatz" in Applets verwendet werden, wo Dateien aus Datenschutz-Gründen nicht zugelassen sind.




  Es folgt das Beispiel RingForm mit einer Superschnittstelle Upper, zwei Upper erweiternden Subschnittstellen Left, Right und einer alle drei Schnittstellen implementierenden Klasse RingForm. Daraus ergibt sich eine ringförmig geschlossene Hierarchie, die entsprechend mit Plot-Graphik auf dem Output ausgegeben wird.


  RingForm ist nicht nur ein Beispiel für "mehrfache Erbschaft", d.h. RingForm erbt von Left, Right und Upper, sondern auch für "mehrdeutige Erbschaft", d.h. der gleiche Bezeichner Text wird für verschiedene Datenfelder in Left, Right und Upper verwendet, und für "mehrpfadige Erbschaft", d.h. RingForm erbt über drei verschiedene Pfade von Upper.
//************************ RingForm.java *************************
//                                                               *
//     Klasse mit Schnittstellen in ringfoermiger Hierarchie:    *
//                                                               *
//       "mehrfache und mehrdeutige Erbung",                     *
//          d.h. RingForm erbt Text aus Left, Upper, Right,      *
//                                                               *
//       "mehrpfadige Erbung",                                   *
//          d.h. RingForm erbt ueber drei Pfade von Upper.       *
//                                                               *
//****************************************************************

interface Upper               {String Text="Upper";}

interface Left  extends Upper {String Text="Left" ;}

interface Right extends Upper {String Text="Right";}

class RingForm implements Left,Upper,Right
 {
  public static void main(String[] args)         // args ungenutzt
   {System.out.print(" ,--"    +  Upper.Text+"---,"     +"\r\n"+
                     " | "     +"   |    "  +  " |"     +"\r\n"+
                      Left.Text+ "  |  "    + Right.Text+"\r\n"+
                     " | "     +"   |    "  +  " |"     +"\r\n"+
                     " `-"     +"RingForm"  +  "-'"     +"\r\n");
   }
 }
//****************************************************************

| Output      
+--------------
| ,--Upper---,
| |    |     |
|Left  |  Right
| |    |     |
| `-RingForm-'  
  Das folgende Programm Generic zum Werte-Tausch der Komponenten eines Paars demonstriert "späte Bindung" nicht nur am Beispiel der generischen Klasse (9.1) Typ mit Typ-Parameter T, sondern auch an der generischen Schnittstelle Paar mit Typ-Parameter P, an dem generischen Konstruktor (9.1.2) Typ() mit Typ-Parameter K und der generischen Methode (7.1) swap() mit Typ-Parameter S.

  Typ-Parameter-Klauseln findet man in den Syntaxdiagrammen der Vereinbarungen von normalen Klassen (Syntax.091), normalen Schnittstellen (Syntax.096), Konstruktoren (Syntax.087/089) und Methoden (Syntax.083).

  Typ-Argument-Klauseln findet man in den Syntaxdiagrammen der Erzeugung von Klassen (Syntax.032/092) und des Aufrufs von Konstruktoren (Syntax.088) und Methoden (Syntax.085).


//************************ Generic.java **************************
//                                                               *
//             Werte-Tausch der Komponenten eines Paars          *
//                                                               *
//      generische  Klasse        Duo    mit Typ-Parameter D     *
//                                                               *
//      generische  Schnittstelle Put    mit Typ-Parameter P     *
//                                                               *
//      generischer Konstruktor   Duo()  mit Typ-Parameter K     *
//                                                               *
//      generische  Methode       swap() mit Typ-Parameter S     *
//                                                               *
//****************************************************************


interface Put<P>                                // Typ-Parameter P
 {void put(P[] p);
 }

class Duo<D> implements Put<D>                  // Typ-Parameter D 
 {
  public void put(D[] d)                        
   {System.out.print  ("{"+d[0]+","+d[1]+"}");}

  <S>void swap(S[] s)                           // Typ-Parameter S
   {S                  memo=s[0];
                       s[0]=s[1];
                       s[1]=memo;
   }

  <K>Duo(K k,D[] d)                             // Typ-Parameter K
   {put               (d);
    this.<D>swap      (d);
    System.out.print  (" "+k+" ");
    put               (d);
    System.out.println();
   }
 }

class Generic
 {public static void main(String[] args)        // args ungenutzt
   {
    //      K         D         K         D
    new<String>   Duo<Double>("swap",new Double[]{ 2.72 , 3.14 });
    new<Character>Duo<String>('>'   ,new String[]{"Dick","Doof"});

   }
 }

//****************************************************************

| Output                                                     
+----------------------------
|{2.72,3.14} swap {3.14,2.72}
|{Dick,Doof} > {Doof,Dick}
9.2.3    Anmerkungstyp (Annotation)

  Eine Anmerkungstyp-Vereinbarung (AnnotationTypeDeclaration) ist nach Syntaxdiagramm 098 (hier Erweiterung 098° durch Einsetzung von AnnotationTypeElementDeclaration Syntax.097) von der Form
098°:AnnotationTypeDeclaration    AnmerkungsTypVereinbarung
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
H   ,------------------------,                                   H
H   |  ,-------------------, |                                   H
H ->+->| InterfaceModifier |-'                                   H
H   |  '-------------------'                                     H
H   |                      Annotation-   implicitely extends     H
H   |                    ,-----------,  annotation.Annotation    H
H   '-> @ -> interface ->| Identifer |------------------------,  H
H                        '-----------'                        |  H
H ,-----------------------------------------------------------'  H
H |   A   n  n  o  t  a  t  i  o  n   T  y  p  e   B  o  d  y    H
H |      ,--------------------------------------------,          H                          
H |      | A n notation T y p e E lement D eclaration |          H
H |      |      ,------------------------,            |          H
H '-> { -+>---->|ConstantFieldDeclaration|------------| ,-> } -> H
H        | |    '------------------------'            | |        H
H        | | ,-----------------------------,          | |        H
H        | | |  ,------------------------, |          | |        H
H        | |>+->| AbstractMethodModifier |-'          | |        H
H        | | |  '------------------------'            | |        H
H        | | |  ,--------,   Method-   empty Formal-  | |        H
H        | | |  |Annotati|  ,------,   Parameter-     | |        H
H        | | |  |onResult|  |Identi|   Clause         | |        H
H        | | '->| -Type  |->|-fier |-> ( -> ) -,      | |        H
H        | |    '--------'  '------'           |      | |        H
H        | | ,---------------------------------|      | |        H
H        | | |             ,--------------,    v      | |        H
H        | | '-> default ->| ElementValue |------> ; -| |        H
H        | |               '--------------'           | |        H
H        | |     inner                                | |        H
H        | |    ,-----------------------------,       | |        H
H        | '--->| ClassOrInterfaceDeclaration |-------' |        H
H        |      '-----------------------------'         |        H
H        '----------------------------------------------'        H
H                                                                H
H An  AnnotationResultType  is a  Ein  AnmerkungsErgebnisTyp ist H
H a  PrimitiveType   or  String,  ist ein GrundTyp oder Zeichen- H
H Class   or  Class  Invocation,  kette, Class bzw.Class Aufruf, H
H EnumType,  AnnotationType,  or  AufzählungsTyp,  AnmerkungsTyp H
H an  array   of  one  of  these  oder eine  Reihung  aus  einem H
H types.                          dieser Typen.                  H
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH

 z.B. (siehe MyAnnotation unten)
      public           // InterfaceModifier
       @interface       // at-sign Keyword
         MyCopyright     // AnnotationIdentifier
          {               // AnnotationTypeBody Begin 
           String          // ResultType of AnnotationMethod
            value           // MethodIdentifier
             ()              // empty FormalParameterClause
              default "my";   // default ElementValue (Syntax.099)
          }               // AnnotationTypeBody End
  Mehrere AnmerkungsTyp sind (z.T. Compiler-abhängig) vordefiniert:
      annotation.Documented
      annotation.Inherited
      annotation.Retention
      annotation.Target
  Nach Konvention ist value der Methoden-Bezeichner in einem Einzel-Element.

  Eine Anmerkung (Annotation) ist nach Syntaxdiagramm 100 von der Form
100: Annotation                   Anmerkung
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
H     AnnotationType-                                            H
H        ,------,                                                H
H -> @ ->| Name |-,                                              H
H        '------' |                                              H
H ,---------------'                                              H
H |         E l e m e n t  V a l u e  P a i r L i s t            H
H |         ,----------------- , <------------------,            H
H |         | E  l  e  m  e  n  t V a l u e P a i r |            H
H |         |   Element-                            |            H
H |         | ,------------,       ,--------------, |            H
H |-> ( --->->| Identifier |-> = ->| ElementValue |-->--> ) -,   H
H |      |    '------------'       '--------------'   |      |   H
H |      |                  S i n g l e E l e m e n t |      |   H
H |      |                         ,--------------,   |      |   H
H |      |------------------------>| ElementValue |---|      |   H            
H |      |                         '--------------'   ^      |   H
H |      |                    E m p t y E l e m e n t |      |   H
H |      '--------------------------------------------'      |   H
H |  M a r k e r A n n o t a t i o n                         |   H
H '--------------------------------------------------------->--> H
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH

 z.B. (siehe MyAnnotation unten)
      @MyCopyright  // at-sign AnnotationTypeName
        (           // ElementClause Begin        
         "MyMethod" // ElementValue (Syntax.099) of SingleElement
        )           // ElementClause End


  Im folgenden ersten Beispiel MyAnnotation werden eine Klasse, ein Konstruktor, ein Datenfeld und eine Methode mit einer selbstdefinierten Anmerkung mit Element-Klausel @MyCopyright("...") versehen.
//********************* MyAnnotation.java ************************
//  Meine Klasse, Konstruktor, Datenfeld und Methode werden mit  *
//  selbstdefinierten Anmerkungen @MyCopyright("...") versehen.  *
//     MyCopyright.java muss im aktuellen Verzeichnis stehen.    *
//****************************************************************

@MyCopyright(/*"my" default*/)  class  MyClass
 {@MyCopyright("myConstructor")        MyClass() {};
  @MyCopyright("myField")       String myField="alles mein!";
  @MyCopyright("myMethod")      void   myMethod(String s)
                                        {System.out.println(s);}
 }

class MyAnnotation
 {public static void main(String[] args)         // args ungenutzt
   {new MyClass().myMethod(new MyClass().myField);}
 }

//****************************************************************

| Output
+-----------
|alles mein!



  Der benutzerdefinierte Anmerkungstyp public @interface MyCopyright muss vom Benutzer in einer Datei mit Namen MyCopyright.java im Paket bzw. im aktuellen Verzeichnis vereinbart werden.
//********************* MyCopyright.java *************************
//       Anmerkungstyp-Vereinbarung fuer MyAnnotation.java       *
//****************************************************************

public @interface MyCopyright
 {String value() default "my";
 }
//****************************************************************


  Im folgenden zweiten Beispiel PredefinedAnnotation werden eine Klasse, ein Konstruktor, ein Datenfeld und eine Methode mit der vordefinierten Anmerkung @Deprecated versehen.
//****************** PredefinedAnnotation.java *******************
//  Nicht empfohlene Klasse, Konstruktor, Datenfeld und Methode  *
//    werden mit vordefinierter Anmerkung @Deprecated versehen.  *
//****************************************************************

@Deprecated   class  DeprClass
 {@Deprecated        DeprClass() {};
  @Deprecated String deprField="alles nicht empfohlen!";
  @Deprecated void   deprMethod(String s)
                      {System.out.println(s);}
 }
class PredefinedAnnotation 
 {public static void main(String[] args)         // args ungenutzt
   {new DeprClass().deprMethod(new DeprClass().deprField);}
 }
//****************************************************************
| Command                             | Output
+---------------------------------    +---------------------------
|javac PredefinedAnnotation           |Recompile with 
|                                     |      -Xlint:deprecation ..
+---------------------------------    +---------------------------
|javac -Xlint:                        |                         ..
| deprecation PredefinedAnnotation    |DeprClass  .. deprecated ..                                |..Typ()    ..deprecated..
|                                     |DeprClass().. deprecated ..                               |..Typ()    ..deprecated..
|                                     |deprField  .. deprecated ..
|                                     |deprMethod .. deprecated ..
|                                     |warnings                 ..
+---------------------------------    +---------------------------
|java PredefinedAnnotation            |alles nicht empfohlen!
  Der vordefinierte Anmerkungstyp public @interface Deprecated (siehe AnnotationTypeDeclaration Syntax.098, oben) steht im Paket java.lang. @Deprecated ist eine MarkerAnnotation (siehe Annotation oben, Syntax.100) d.h. ohne Element-Klausel.

  Anmerkungstypen sind Schnittstellen. Jeder Anmerkungstyp ist Subschnittstelle der Superschnittstelle Annotation aus dem Paket java.lang.annotation.

  Anmerkungstypen werden verwendet, um Vereinbarungen mit Anmerkungen zu versehen, z.B. Copyright (siehe oben Beispiel MyAnnotation) in Form einer Anmerkung, die bei der Übersetzung des Programms mit in die class-Datei aufgenomen wird. Im Gegensatz dazu würde Copyright in Form von Kommentar bei der Übersetzung des Programms nicht mit in die class-Datei aufgenommen werden. Anmerkungen bieten Vorteile für die Gewinung von Meta-Information über Java-Prome, dürfen aber die Semantik von Java-Programmen nicht beeinflussen.



9.3      Testfragen
zu    Frage                                  | abdeckbare Antwort
---------------------------------------------+--------------------
                                             |
9.1.1    Kann  ein Mitglied einer Klasse den | ja                  
      gleichen  Bezeichner haben wie ein Ob- |                    
      jekt dieser Klasse?                    |                     
                                             |
9.1.2   Welche der 4 Vereinbarungen in K ist |           
      eine Methode, welche ein Konstruktor?  |
                                             |
      class K                                |
       {                                     |
        void   P(int i){System.out.print(1);}| Methode (Prozedur)
        int    F(int i){return           2 ;}| Methode (Funktion)
                                             |
        public K()     {System.out.print(3);}| Konstruktor
               K(int i){System.out.print(4);}| Konstruktor
       }                                     |
                                             |
9.1.3    Wo  sind  Klassenmitglieder  direkt |
      unter  ihrem  Bezeichner  zugreifbar , |
      wenn sie modifiziert sind als          |
                                             |  
         private                             | in aktueller Klasse
                                             |  
         protected                           | in aktueller Klasse
         public                              |  und in Subklassen
                                             |
9.1.3    Wo  sind Klassenmitglieder indirekt |
      unter  ihrem  qualifizierten Namen zu- |
      greifbar,wenn sie modifiziert sind als |
                                             |  
         private                             | in aktueller Klasse
                                             |  
         protected                           | im aktuellen Paket
                                             |
         public                              | im aktuellen und in
                                             |  Observer-Paketen
                                             |              
9.1.4    Was ist eine abstrakte Klasse?      | Klasse mit Modifi-
                                             |  zierer abstract
                                             | bzw. mit abstrakter
                                             |  Methoden-Vereinba- 
                                             |  rung
                                             |
9.1.5    Ist  das folgende Programm Test mit | 
      javac Test.java  übersetzbar  und  was | Der Übersetzer 
      liefert der Interpreter java Test ?    |  javac Test.java  
                                             |  meldet kein.Fehler 
                                             | 
      class Type                             | 
       {Type field=new Type();               | Der Interpreter 
       }                                     |  java Test meldet
      class Test                             |  StackOverflowError
       {public static void main(String[]args)|  wegen  unendlicher
        {Type object=new Type();}            |  new-Erzeugung  bei
       }                                     |  der Type-Rekursion
                                             |
9.1.6    Die  Aufzählung  Windrose  ist  wie |
      folgt vereinbart                       |
                                             |
         enum Windrose {NORD,OST,SUED,WEST}  |
                                             |
      # Ist Windrose eine Klasse?            | ja
                                             |
      # Wird Windrose implizit instantiiert? | ja
                                             |
      # Kann man zu Windrose Objekte bilden? | nein
                                             |
      # Was ist das Resultat von             |
                                             |
        Windrose.OST==Windrose.valueOf("OST")| true
        Windrose.OST==Windrose.values()[1]   | true
        Windrose.OST.name().equals("OST")    | true
                                             |
9.2.2   Welche  der folgenden Vereinbarungen |
      sind korrekt  (C1,C2 seien vereinbarte |
      Klassen,  I1,I2  vereinbarte  Schnitt- |
      stellen)?                              |
                                             |
      class C extends C1 implements I1,I2 {} | ja
      interface I        extends    I1    {} | ja
                                             |
      class C extends C1,C2               {} | nein:2mal extends 
      interface I        implements I1    {} | nein:impl.inkorrekt
                                             |
9.2.2   Kann man in RingForm.java entweder   |
                                             '----------------,
      #      bei "interface Left         extends Upper"       |
        oder bei "interface Right        extends Upper"       | 
                         verzichten auf "extends Upper"       | ja
                                                              |
      # und  bei "class RingForm implements Left,Upper,Right" |
                         verzichten auf         "Upper,"     ,| ja
                                             ,----------------'
        ohne dass der Output sich ändert?    |
                                             |
9.2.3    Die  Anmerkung  OpenSource  ist wie |
      folgt vereinbart                       |
                                             |
         public @interface OpenSource {}     |
                                             |
      # Ist OpenSource eine Schnittstelle?   | ja
                                             |
      # Ist OpenSource eine MarkerAnnotation?| ja
                                             |
      # Wo muss OpenSource vereinbart werden?| in Datei mit Namen
                                             |  OpenSource.java
      # Verseh HelloWorld in HelloWorld.java | @OpenSource class
         mit der Anmerkung OpenSource        |  HelloWorld {...}
                                             |
    Java Programmierung
  Kap.10