From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001 From: Sven Eisenhauer Date: Fri, 10 Nov 2023 15:11:48 +0100 Subject: add new repo --- .../hjp5/html/k100105.html | 1099 ++++++++++++++++++++ 1 file changed, 1099 insertions(+) create mode 100644 Master/Reference Architectures and Patterns/hjp5/html/k100105.html (limited to 'Master/Reference Architectures and Patterns/hjp5/html/k100105.html') diff --git a/Master/Reference Architectures and Patterns/hjp5/html/k100105.html b/Master/Reference Architectures and Patterns/hjp5/html/k100105.html new file mode 100644 index 0000000..58109c6 --- /dev/null +++ b/Master/Reference Architectures and Patterns/hjp5/html/k100105.html @@ -0,0 +1,1099 @@ + + + +Handbuch der Java-Programmierung, 5. Auflage + + + + + + + + + +
 Titel  + Inhalt  + Suchen  + Index  + DOC  +Handbuch der Java-Programmierung, 5. Auflage +
 <<  +  <   +  >   + >>  + API  +Kapitel 15 - Collections II +
+
+ + + + +

15.8 Typisierte Klassen und generische Collections +

+
+ +
+ + + + +

15.8.1 Grundlagen

+

+ + + + + + + + + + + +
+ +

+Dieser Abschnitt beschreibt eine Erweiterung, die seit der J2SE 5.0 +zur Verfügung steht und unter dem Namen »Generics« +bekannt geworden ist. Es geht dabei vordergründig um die Möglichkeit, +typsichere Collection-Klassen zu definieren. Also solche, in die nicht +nur allgemein Objekte des Typs Object +gesteckt werden können, sondern die durch vorhergehende Typisierung +sicherstellen, dass nur Objekte des korrekten Typs (etwa Integer +oder String) +eingefügt werden können. Diese, von vielen Java-Entwicklern +seit langer Zeit geforderte, Spracherweiterung bringt zwei wichtige +Vorteile: +

    +
  • Da die Einfügeoperationen typsicher sind, kann zum Zugriffszeitpunkt +kein fehlerhaft typisiertes Objekt mehr in der Collection sein. Bereits +der Compiler stellt sicher, dass nur Objekte eingefügt werden +können, die zur Collection passen. Ausnahmen des Typs ClassCastException, +die beim späteren Auslesen von fehlerhaft typisierten Collection-Elementen +auftreten würden (also erst zur Laufzeit des Programms), gehören +der Vergangenheit an. +
  • Da der Compiler den Typ der Collection kennt, kann die umständliche +Typkonvertierung beim Auslesen der Collection-Elemente entfallen, +und der Programmcode wird kürzer und lesbarer. +
+
+ + + + +
 JDK1.1-6.0 
+
+

+ + + + + + + + + +
+ +

+Genau genommen geht es nicht nur um Collections im eigentlichen Sinne, +sondern um die Typisierung von beliebigen Java-Klassen. Also die Möglichkeit, +festzulegen, dass eine bestimmte Klasse X +zwar so implementiert wurde, dass sie prinzipiell mit allen +anderen Klassen zusammen arbeitet (bzw. Objekte deren Typs aufnimmt), +im konkreten Anwendungsfall von X +aber die Möglichkeit besteht, die Zusammenarbeit (etwa aus Sicherheits- +oder Konsistenzgründen) auf eine fest vorgegebene andere Klasse +zu beschränken.

+ + + + +
 Hinweis 
+
+ +

+Was sich etwas kompliziert anhört, wollen wir durch ein einfaches +Beispiel illustrieren: + + +

+ + + + +
+ +
+001 public static void printSorted1(int... args)
+002 {
+003   Vector v = new Vector();
+004   for (int i = 0; i < args.length; ++i) {
+005     v.addElement(new Integer(args[i]));
+006   }
+007   Collections.sort(v);
+008   for (int i = 0; i < v.size(); ++i) {
+009     int wert = 10 * ((Integer)v.elementAt(i)).intValue();
+010     System.out.print(wert + " ");
+011   }
+012   System.out.println();
+013 }
+
+
+ +Listing 15.8: Eine untypisierte Sortiermethode

+ +

+printSorted1 bekommt als Parameter +eine Menge von Ganzzahlen übergeben und hat die Aufgabe, diese +mit 10 zu multiplizieren und sortiert auf der Konsole auszugeben. +Die Methode legt dazu einen Vector v +an und fügt in diesen zunächst die in Integer +konvertierten int-Werte +ein. Anschließend sortiert sie den Vector, liest die Integer-Objekte +aus, konvertiert sie in int-Werte +zurück und gibt die mit 10 multiplizierten Ergebnisse auf der +Konsole aus. + +

+Seit der J2SE 5.0 kann die Methode nun typsicher gemacht werden: + + +

+ + + + +
+ +
+001 public static void printSorted2(int... args)
+002 {
+003   Vector<Integer> v = new Vector<Integer>();
+004   for (int i = 0; i < args.length; ++i) {
+005     v.addElement(new Integer(args[i]));
+006   }
+007   Collections.sort(v);
+008   for (int i = 0; i < v.size(); ++i) {
+009     int wert = 10 * v.elementAt(i).intValue();
+010     System.out.print(wert + " ");
+011   }
+012   System.out.println();
+013 }
+
+
+ +Listing 15.9: Die typsichere Version der Sortiermethode

+ +

+Der Vector +wurde hier mit einem Typ-Parameter versehen, der in spitzen Klammern +angegeben wird: + +

+Vector<Integer> v = new Vector<Integer>();
+
+ + +

+Dadurch wird dem Compiler mitgeteilt, dass dieser Vector +ausschließlich Integer-Objekte +aufnehmen kann. Alle Versuche, darin einen String, +ein Double +oder irgendein anderes Nicht-Integer-Objekt zu speichern, werden vom +Compiler unterbunden. Auch der zweite der oben genannten Vorteile +kommt zum Tragen: beim Zugriff auf Vector-Elemente +mit Hilfe der Methode elementAt +werden diese automatisch in ein Integer +konvertiert, der übliche Typecast kann also entfallen. + +

+Auf diese Weise können nun seit der J2SE 5.0 alle Collection-Klassen +typsicher verwendet werden: einfach den Datentyp in spitzen Klammern +direkt hinter dem Klassennamen angeben! Auch bei Collections, die +mit mehr als einem Parameter arbeiten, ist das möglich, also +inbesondere bei den verschiedenen Maps. Hier werden beide Parameter +in spitzen Klammern angegeben und durch Kommata voneinander getrennt. +Wir werden dazu später ein Beispiel sehen. + +

+Zunächst soll jedoch das obige Beispiel weiter vereinfacht werden. +Tatsächlich ist printSorted2 +nämlich etwas länger als printSorted1, +d.h. wir haben uns die Typsicherheit durch zusätzlichen +Code erkauft. Daß es wesentlich einfacher geht, zeigt folgende +Variante: + + +

+ + + + +
+ +
+001 public static void printSorted3(int... args)
+002 {
+003   Vector<Integer> v = new Vector<Integer>();
+004   for (int i : args) {
+005     v.addElement(i); 
+006   }
+007   Collections.sort(v);
+008   for (Integer i : v) {
+009     System.out.print((i * 10) + " "); 
+010   }
+011   System.out.println();
+012 }
+
+
+ +Listing 15.10: Die vereinfachte Version der typsicheren Variante

+ +

+Hier kommen zusätzlich folgende Techniken zum Einsatz: +

+ +

+Dieses Programm sieht wesentlich besser aus als die erste Fassung. +Es ist nun sowohl typsicher als auch besser lesbar. Möglich gemacht +wird dies durch verschiedene Neuerungen der J2SE 5.0, die hier im +Zusammenspiel ihr Synergiepotential entfalten. Autoboxing und Autounboxing +werden in Abschnitt 10.2.3 erläutert +und die erweiterte for-Schleife +in Abschnitt 6.3.3. Auch +die variablen Parameterlisten sind eine Neuerung der J2SE 5.0; sie +werden in Abschnitt 7.3.4 +erläutert. + + + + +

15.8.2 Collections mit mehreren Typparametern

+ +

+Wie bereits erwähnt können auch Collections typsicher gemacht +werden, deren Methoden üblicherweise mehr als einen Parameter +erwarten. Ein gutes Beispiel dafür ist das Interface Map +und dessen implementierende Klassen (etwa HashMap, +TreeMap +oder Hashtable). +Sie speichern nicht einzelne Werte, sondern Schlüssel-Wert-Paare. +Soll eine solche Klasse typsicher verwendet werden, sind bei der Deklaration +zwei Typ-Parameter anzugeben: + +

+Hashtable<String, Integer> h = new Hashtable<String, Integer>();
+
+ + +

+An die Einfügeoperationen, die beide Parameter erwarten, muss +nach einer solchen Deklaration zwangsweise ein String und ein Integer +übergeben werden. Die Zugriffsmethoden dagegen erwarten einen +String +als Schlüssel und liefern einen Integer +als Rückgabewert. Beispielhaft wollen wir uns eine Methode ansehen, +die eine Liste von Strings erwartet und dann zählt, wie oft jedes +einzelne Wort darin vorkommt. Eine Pre-5.0-Implementierung könnte +so aussehen: + + +

+ + + + +
+ +
+001 public static void wordCount1(String[] args)
+002 {
+003   Hashtable h = new Hashtable();
+004   for (int i = 0; i < args.length; ++i) {
+005     int cnt = 1;
+006     if (h.containsKey(args[i])) {
+007       cnt = 1 + ((Integer)h.get(args[i])).intValue();
+008     }
+009     h.put(args[i], new Integer(cnt));
+010   }
+011   System.out.println(h);
+012 }
+
+
+ +Listing 15.11: Ein untypisierter Wortzähler

+ +

+Für jedes Element des Parameter-Arrays wird geprüft, ob +es schon in der Hashtable +h enthalten ist. Ist das der +Fall, wird das Wort als Schlüssel verwendet, der zugehörige +Zählerstand aus h gelesen +und um 1 erhöht. Ist das nicht der Fall, wird der Zähler +mit 1 initialisiert. Anschließend wird der Zählerwert mit +dem Wort als Schlüssel in die Hashtable geschrieben. + +

+Seit der J2SE 5.0 kann man die Methode stark vereinfachen: + + +

+ + + + +
+ +
+001 public static void wordCount2(String... args)
+002 {
+003   Hashtable<String, Integer> h = new Hashtable<String, Integer>();
+004   for (String key : args) {
+005     if (h.containsKey(key)) {
+006       h.put(key, 1 + h.get(key));
+007     } else {
+008       h.put(key, 1);
+009     }
+010   }
+011   System.out.println(h);
+012 }
+
+
+ +Listing 15.12: Die 5.0-Wortzählervariante

+ +

+Auch hier machen wir uns gleich alle drei oben genannten Erweiterungen +der J2SE 5.0 zu Nutze. Zudem gibt es einen weiteren Vorteil. Da nun +die Datentypen der Methoden put +und get +bekannt sind, können wir - dank der Verkürzung durch Autoboxing +und Autounboxing - die Programmstruktur übersichtlicher machen. +Wir schreiben dazu die put- +und get-Operationen +in eine Zeile, die Hilfsvariable cnt +wird gar nicht mehr gebraucht. + + + + +

15.8.3 Eine eigene typisierte Listenklasse

+ +

+Nachdem wir uns in den vorherigen Abschnitten angesehen haben, wie +generische Collections verwendet werden, wollen wir nun eine eigene +generische Listenklasse implementieren. Deren Interface soll bewußt +schlank gehalten werden, um unnötige Verkomplizierungen zu vermeiden. +Es besteht aus je einer Methode, um Elemente einzufügen und auszulesen, +einer Methode zur Abfrage der Größe und aus einem Iterator, +um die Elemente (u.a. mit den neuen foreach-Schleifen der J2SE 5.0) +durchlaufen zu können. + +

+Der Einfachheit halber wollen wir die Liste mit einem Array als interne +Datenstruktur realisieren und definieren dazu folgende Klasse: + + +

+ + + + + +
+ +
+001 import java.util.*;
+002 
+003 /**
+004  * Die folgende Klasse realisiert eine einfache Liste mit einer
+005  * festen Größe. Die Liste kann typisiert werden, so dass
+006  * Zugriffs- und Hinzufügemethoden typsicher werden. Darüber
+007  * hinaus implementiert sie das Interface Iterable und stellt
+008  * einen typsicheren Iterator zur Verfügung, um die Verwendung
+009  * in J2SE-5.0-foreach-Schleifen zu ermöglichen.
+010  */
+011 public class MiniListe<E>
+012 implements Iterable<E>
+013 {
+014   private Object[] data;
+015   private int      size;
+016 
+017   /**
+018    * Erzeugt eine leere Liste, die maximal maxSize Elemente
+019    * aufnehmen kann.
+020    */
+021   public MiniListe(int maxSize)
+022   {
+023     this.data = new Object[maxSize];
+024     this.size = 0;
+025   }
+026 
+027   /**
+028    * Fügt ein Element zur Liste hinzu. Falls diese schon
+029    * voll ist, wird eine Exception ausgelöst.
+030    */
+031   public void addElement(E element)
+032   {
+033     if (size >= data.length) {
+034       throw new ArrayIndexOutOfBoundsException();
+035     }
+036     data[size++] = element;
+037   }
+038 
+039   /**
+040    * Liefert die Anzahl der Elemente in der Liste.
+041    */
+042   public int size()
+043   {
+044     return size;
+045   }
+046 
+047   /**
+048    * Liefert das Element an Position pos. Falls kein solches
+049    * Element vorhanden ist, wird eine Exception ausgelöst.
+050    */
+051   public E elementAt(int pos)
+052   {
+053     if (pos >= size) {
+054       throw new NoSuchElementException();
+055     }
+056     return (E)data[pos];
+057   }
+058 
+059   /**
+060    * Liefert einen Iterator zum Durchlaufen der Elemente.
+061    */
+062   public Iterator<E> iterator()
+063   {
+064     return new Iterator<E>()
+065     {
+066       int pos = 0;
+067 
+068       public boolean hasNext()
+069       {
+070         return pos < size;
+071       }
+072       public E next()
+073       {
+074         if (pos >= size) {
+075           throw new NoSuchElementException();
+076         }
+077         return (E)data[pos++];
+078       }
+079       public void remove()
+080       {
+081         throw new UnsupportedOperationException();
+082       }
+083     };
+084   }
+085 
+086   //------------------------------------------
+087   public static void main(String[] args)
+088   {
+089     //Untypisierte Verwendung
+090     MiniListe l1 = new MiniListe(10);
+091     l1.addElement(3.14);
+092     l1.addElement("world");
+093     for (Object o : l1) {
+094       System.out.println(o);
+095     }
+096     //Ganzzahlige Typisierung
+097     System.out.println("---");
+098     MiniListe<Integer> l2 = new MiniListe<Integer>(5);
+099     l2.addElement(3);
+100     l2.addElement(1);
+101     l2.addElement(4);
+102     for (Integer i : l2) {
+103       System.out.println(i + 1000);
+104     }
+105     //Verwendung read-only
+106     System.out.println("---");
+107     MiniListe<? extends Number> l3 = l2;
+108     for (Number i : l3) {
+109       System.out.println(i.intValue() + 1000);
+110     }
+111   }
+112 }
+
+
+MiniListe.java
+ +Listing 15.13: Eine eigene typisierte Listenklasse

+ +

+Die Ausgabe des Programms ist: + +

+3.14
+world
+---
+1003
+1001
+1004
+---
+1003
+1001
+1004
+
+ + +

+Wir wollen uns einige interessante Implementierungsdetails ansehen: +

+ +

+Damit ist die komplette Schnittstelle der Klasse typsicher, und wir +können sie wie in den vorigen Abschnitten beschrieben verwenden. +Die main-Methode +zeigt einige Anwendungen, die nach den bisherigen Ausführungen +selbsterklärend sein sollten. +

+ + + + + + + + + + + +
+ +

+Zu beachten ist, dass in diesem Beispiel »nur« die öffentliche +Schnittstelle der Klasse typsicher ist. Innerhalb der Klasse selbst +ist es nach wie vor möglich, fehlerhaft typisierte Werte in das +Datenarray einzufügen, denn als Object[] +kann es beliebige Objekte aufnehmen. Wir könnten eine solche +Schnittstelle sogar öffentlich machen: + +

+public void addStringElement(String s)
+{
+  if (size >= data.length) {
+    throw new ArrayIndexOutOfBoundsException();
+  }
+  data[size++] = s;
+}
+
+ + +

+Dieser Code wäre vollkommen korrekt und würde vom Compiler +nicht beanstandet werden. Ein Aufruf der Methode in einer MiniListe<Double> +würde tatsächlich einen String +einfügen, und beim Zugriff auf dieses Element würde es zu +einer ClassCastException +kommen. »Typsichere« Klassen sind also nur dann wirklich +typsicher, wenn die Implementierung sicherstellt, dass keine typfremden +Werte gespeichert werden.

+ + + + +
 Warnung 
+
+ + + + +

15.8.4 Typkompatibilität

+ + + + +

Ober- und Unterklassen in generischen Typen

+ +

+Beim Umgang mit typisierten Collections gibt es einige Besonderheiten, +die zu einer Verkomplizierung des ursprünglichen Mechanismus +geführt haben. Wir wollen zunächst eine einfache Methode +betrachten, um uns noch einmal das Zusammenspiel zwischen Ober- und +Unterklassen anzusehen (es wurde unter dem Stichwort »Polymorphismus« +bereits in den Abschnitten Abschnitt 7.1.6 +und Abschnitt 8.4 erläutert): + + +

+ + + + +
+ +
+001 public static void doesWork1()
+002 {
+003   Double pi = new Double(3.14);
+004   Number num = pi;
+005   System.out.println(num.toString());
+006   Double pi2 = (Double)num;
+007 }
+
+
+ +Listing 15.14: Funktionierendes Zusammenspiel von Ober- und Unterklassen

+ +

+Zunächst wird eine Double-Variable +pi angelegt und mit dem Fließkommawert +3.14 initialisiert. In der nächsten Zeile machen wir uns die +Tatsache zunutze, dass Double +eine Unterklasse von Number +ist und weisen der Variablen der Oberklasse einen Wert der Unterklasse +zu. Dies entspricht unserem bisherigen Verständnis von objektorientierter +Programmierung, denn ein Double +ist eine Number, +hat alle Eigenschaften von Number +(und ein paar mehr), und kann daher problemlos als Number +verwendet werden. Die nächsten beiden Zeilen beweisen, dass diese +Annahme korrekt ist (der Inhalt von num +ist tatsächlich 3.14) und dass man die Number-Variable +auch zurückkonvertieren kann - in Wirklichkeit zeigt sie ja auf +ein Double. +

+ + + + + + + + + + + +
+ +

+Überträgt man das Beispiel auf typisierte Collections, lassen +sich die dahinter stehenden Annahmen nicht ohne weiteres aufrecht +erhalten. Ein Vector<Double> +ist kein Subtyp eines Vector<Number>! +Die folgenden Codezeilen sind illegal und werden vom Compiler abgewiesen: + +

+Vector<Double> vd = new Vector<Double>();
+Vector<Number> vn = vd;
+
+ +
+ + + + +
 Warnung 
+
+ +

+Wir wollen uns ansehen, warum das so ist. Wären sie nämlich +erlaubt, könnten wir folgende Methode schreiben: + + +

+ + + + +
+ +
+001 public static void doesntWork1()
+002 {
+003   Vector<Double> vd = new Vector<Double>();
+004   Vector<Number> vn = vd;
+005   vn.addElement(new Integer(7));
+006   Double x = vd.elementAt(0);
+007 }
+
+
+ +Listing 15.15: Nicht funktionierendes Zusammenspiel von Ober- und +Unterklassen

+ +

+Das Programm erzeugt einen Vector<Double> +vd und weist ihn einer Vector<Number>-Variable +vn zu. Wäre diese eine Oberklasse von Vector<Double>, +könnten wir natürlich auch Integer-Werte +in den Vector +einfügen wollen. Denn auch Integer +ist eine Unterklasse von Number, +und mit derselben Berechtigung würden wir annehmen, dass Vector<Number> +Oberklasse von Vector<Integer> +ist. Dann wäre aber nicht mehr sichergestellt, dass beim Zugriff +auf vd nur noch Double-Elemente +geliefert werden, denn über den Umweg vn +haben wir ja auch ein Integer-Objekt +eingefügt. Der Compiler könnte also nicht mehr garantieren, +dass die vierte Zeile korrekt ausgeführt wird. + +

+Daraus ist ein folgenschwerer Schluß zu ziehen: Ist U +eine Unterklasse von O, so folgt +daraus eben nicht, dass auch G<U> +eine Unterklasse von G<O> +ist. Das ist schwer zu verstehen, denn es widerspricht unseren bisherigen +Erfahrungen im Umgang mit Ober- und Unterklassen. Das Problem dabei +ist die Veränderlichkeit des Vectors, denn dadurch könnte +man über den Alias-Zeiger vn +Werte einzufügen, die nicht typkonform sind. Aus diesem Grunde +würde der Compiler bereits die zweite Zeile der obigen Methode +mit einem Typfehler ablehnen. + + + + +

Der Wildcard ? +

+ +

+Diese neue Typinkompatibilität hat nun einige Konsequenzen, die +sich vor allem dort bemerkbar machen, wo wir bisher intuitiv angenommen +haben, dass Ober- und Unterklasse zuweisungskompatibel sind. Ein Beispiel +ist etwa die Parametrisierung von Methoden, bei der man üblicherweise +die formalen Parameter etwas allgemeiner fasst, um die Methode vielseitiger +einsetzen zu können. + +

+Betrachten wir eine Methode zur Ausgabe aller Elemente unserer Zahlenliste. +Mit etwas Weitblick würde man sie so formulieren: + + +

+ + + + +
+ +
+001 public static void printAllNumbers1(List<Number> numbers)
+002 {
+003   for (Number s : numbers) {
+004     System.out.println(s);
+005   }
+006 }
+
+
+ +Listing 15.16: Nicht funktionierende Ausgabe der Zahlenliste

+ +

+Anstelle eines Vector<Double> +verallgemeinern wir auf List<Number>. +In der Annahme, dann nicht nur den Inhalt eines Vektors ausgeben zu +können, sondern auch den einer ArrayList +oder LinkedList +(die beide ebenfalls vom Typ List +sind), und zwar auch dann, wenn sie nicht Double-, +sondern auch Integer- +oder Long-Werte +enthalten (die ebenfalls vom Typ Number +sind). Diese Methode enthält nicht mehr Code als die speziellere +Form, ist aber viel universeller einzusetzen. Ergo würde sich +ein erfahrener Programmierer normalerweise dafür entscheiden, +sie auf diese Weise zu parametrisieren. + +

+Leider hat die Sache einen Haken. Zwar akzeptiert printAllNumbers1 +beliebige Collections vom Typ List, +aber eben nur, wenn sie Werte vom Typ Number +enthalten. Solche mit Double-, +Integer- +oder Long-Werten +lehnt sie - aus den oben beschriebenen Gründen - ab. Der praktische +Nutzen dieser Methode ist damit nur mehr gering. + +

+Um mehr Flexibilität zu gewinnen, wurde der Wildcard »?« +als Typparameter eingeführt. Wird das Fragezeichen anstelle eines +konkreten Elementtyps angegeben, bedeutet dies, das die Collection +beliebige Werte enthalten kann: + + +

+ + + + +
+ +
+001 public static void printAllNumbers2(List<?> numbers)
+002 {
+003   for (Object o: numbers) {
+004     System.out.println(o);
+005   }
+006 }
+
+
+ +Listing 15.17: Funktionierende Ausgabe der Zahlenliste

+ +

+An diese Methode können nun Listen mit beliebig typisierten Elementen +übergeben werden. Allerdings gibt es beim Lesen der Elemente +keine Typsicherheit mehr. Am Kopf der for-Schleife +kann man erkennen, dass der Compiler die Listenelemente nun - wie +in früheren JDK-Versionen - lediglich als Werte des Typs Object +ansieht. Spezielle Eigenschaften sind damit unsichtbar bzw. müssen +mit Hilfe einer expliziten Typkonvertierung wieder sichtbar gemacht +werden. + + + + +

Gebundene Wildcards

+ +

+Eine abgeschwächte Form des »?«-Wildcards sind die +gebundenen Wildcards (»bounded wildcards«). Sie entstehen, +wenn nach dem Fragezeichen das Schlüsselwort extends +angegeben wird, gefolgt vom Namen des Elementtyps. Dadurch wird ausgedrückt, +dass die Collection Elemente der angegebenen Klasse oder einer ihrer +Unterklassen enthalten kann: + + +

+ + + + +
+ +
+001 public static void printAllNumbers3(List<? extends Number> numbers)
+002 {
+003   for (Number s : numbers) {
+004     System.out.println(s.doubleValue());
+005   }
+006 }
+
+
+ +Listing 15.18: Verbesserte funktionierende Ausgabe der Zahlenliste

+ +

+Gebundene Wildcards realisieren am ehesten die bisher bekannten Regeln +von Typkonformität bei Ober- und Unterklassen von Elementen. +? extends O bedeutet, dass die +Collection Elemente des Typs O +oder einer (auch über mehrere Stufen) daraus abgeleiteten Unterklasse +enthalten kann. An einen formalen Parameter vom Typ List<? +extends Number> können also aktuelle Parameter +des Typs Vector<Double>, +ArrayList<Integer> usw. +übergeben werden. +

+ + + + + + + + + +
+ +

+Zu beachten ist allerdings, dass die Wildcards nur das Lesen +der Elemente flexibilisieren. Das Einfügen neuer Elemente ist +dagegen nicht mehr erlaubt und wird vom Compiler unterbunden. +Genauer gesagt, verboten ist der Aufruf von Methoden, die mindestens +einen generisch typisierten Parameter haben. Die Gründe entsprechen +den oben erläuterten. Wäre es nämlich zulässig, +in eine List<? extends Number> +einen Double +einzufügen, so würde ein Problem entstehen, wenn es sich +tatsächlich beispielsweise um einen Vector<Integer> +handeln würde, denn dieser darf ja kein Element des Typs Double +aufnehmen. Daher sorgt der Compiler dafür, dass bei der Verwendung +von Wildcards und gebundenen Wildcards die Collection nur noch zum +Lesen der Elemente verwendet werden darf. Versuche, neue Elemente +einzufügen, werden mit einem Compilerfehler quittiert.

+ + + + +
 Hinweis 
+
+ + + + +

15.8.5 Sonstiges

+ +

+Die Implementierung von generischen Collections ist in Java im Prinzip +Sache des Compilers, die virtuelle Maschine merkt davon nichts. Der +Compiler interpretiert den Quelltext, prüft die Typ-Parameter +und erzeugt Bytecode mit den erforderlichen Typ-Konvertierungen, Warnungen +und Fehlermeldungen. Das Laufzeitsystem, die virtuelle Maschine, arbeitet +dabei im Grunde wie in früheren JDK-Versionen. Dabei bleibt insbesondere +die Integrität der VM stets erhalten und Typfehler führen +zu kontrollierten (ClassCast-) Exceptions, wie in früheren JDK-Versionen. +Trotz allen Aufwands lassen sich nämlich Fälle konstruieren, +bei denen fehlerhaft typisierte Werte in eine generische Collection +eingefügt werden. Wir werden dazu im nächsten Abschnitt +ein einfaches Beispiel sehen. + +

+Anders als Templates in C++ erzeugen generische Java-Klassen keinen +zusätzlichen Programmcode. Alle Instanzen einer generischen Klasse +verwenden denselben Bytecode, getClass +liefert ein und dasselbe Klassenobjekt und die statischen Variablen +werden gemeinsam verwendet. Es ist nicht erlaubt, in statischen Initialisierern +oder statischen Methoden auf den Typparameter einer Klasse zuzugreifen, +und die Anwendung des instanceof-Operators +auf eine typisierte Klasse ist illegal. + +

+Es ist zulässig, generische Collections und herkömmliche +Collections gemeinsam zu verwenden. Erwartet beispielsweise ein Methodenparameter +eine typisierte Collection, so kann auch eine untypisierte Collection +übergeben werden, und umgekehrt. In diesem Fall kann der Compiler +die Typsicherheit des Programmcodes allerdings nicht mehr sicherstellen +und generiert vorsichtshalber eine »unchecked warning« : + +

+Note: Some input files use unchecked or unsafe operations.
+Note: Recompile with -Xlint:unchecked for details.
+
+ + +

+Durch Rekompilieren mit -Xlint:unchecked +werden Detailinformationen ausgegeben. Die Entwickler des JDK gehen +davon aus, dass in Zukunft nur noch typisierte Collections verwendet +werden, und diese Warnungen nach einer gewissen Übergangszeit +der Vergangenheit angehören werden. + +

+Neben den hier beschriebenen Eigenschaften gibt es noch eine ganze +Reihe weiterer Aspekte von generischen Klassen, auf die wir hier nicht +näher eingehen wollen. Sie werden meist gebraucht, um spezielle +Sonderfälle bei der Entwicklung von Collection-Klassen zu realisieren; +für »Otto Normalprogrammierer« sind die meisten von +ihnen weniger relevant. Die nachfolgende Aufzählung listet einige +von ihnen auf, weitere Informationen können der Sprachspezifikation +bzw. der Dokumentation der J2SE 5.0 oder 6.0 entnommen werden: +

+
+ + + +
 Titel  + Inhalt  + Suchen  + Index  + DOC  +Handbuch der Java-Programmierung, 5. Auflage, Addison +Wesley, Version 5.0.1 +
 <<  +  <   +  >   + >>  + API  +© 1998, 2007 Guido Krüger & Thomas +Stark, http://www.javabuch.de +
+ + + -- cgit v1.2.3