GENERICS

Werbung
9. Kapitel GENERICS Techniken der Programmentwicklung Prof. Dr. Wolfgang Schramm Übersicht 1 1.  Programmstrukturierung mit Paketen (packages) 2.  Vererbung 3.  Abstrakte Klassen und Interfaces 4.  Ausnahmebehandlung 5.  Datenströme: die Java IO-­‐Bibliothek 6.  MulSthreading 7.  CollecSons 8.  Innere Klassen 9.  Generics 10.  ReflecSon Lernziele des Kapitels 2 ¨ 
¨ 
¨ 
¨ 
Verstehen warum man (in Java 5) Generics eingeführt hat. Kennenlernen des Generic-­‐
Konzepts. Verstehen, was generisch sein kann. Selbst generische Klassen und Methoden schreiben können. Inhalt 3 1.  Einführung / MoSvaSon 2.  Generics (DefiniSon) 3.  Generische Klassen • 
• 
Subtyping Wildcards 4.  Generische Methoden 5.  Anwendungsbeispiele Beispiel: Einfacher Stack für besSmmte Objekte 5 Stack für String-Objekte
Stack für Integer-Objekte
Schlecht: 2 x quasi derselbe Code für dieselben Aufgaben.
Frage: Kann man das mit unserem Wissen nicht besser machen?
Wenn ja: Wie?
Beispiel: Einfacher Stack für alle Arten von Objekten 6 Laufzeitfehler
ADT Stack: Problem Typsicherheit 7 o 
o 
o 
o 
o 
o 
o 
Der ADT Stack ist prinzipiell offen für jeden Typ. Man speichert Objekte vom allgemeinsten Typ Object. Beim Lesen vom Stack (pop) werden diese allgemeinen Objekte auch wieder zurück geliefert. Wenn man in einem Stack Integer-­‐Objekte speichert, will man dort keine String-­‐Objekte speichern – doch mit einen allgemeinen Typ Object kann das nicht verhindert werden. Abhilfe seit Java 5 : die Nutzung von Generics à bessere Typsicherheit, denn nur ganz spezielle Objekte kommen in die Datenstruktur. Mit den Generics lässt sich bei der KonstrukSon des ADT Stack angeben, welche Objekte in den Stack aufgenommen werden dürfen. Dann ist man auch sicher, welche Objekte man vom Stack zurück bekommt. MoSvaSon 8 o 
o 
o 
o 
Wie abstrahiere ich vom konkreten Typ eines Objekts? Wie kann ich (typsichere) Containerklassen (z.B. einen ADT Stack) bauen, die jedes beliebige Objekt aufnehmen können? Wie baue ich typsichere Frameworks? Wie vermeide ich (unsichere) Casts und mache dadurch meinen Code typsicher? Beispiel: Stack mit Generics 9 Beispiel: Stack mit Generics 10 Compilezeitfehler
Generische Typen – Warum? 11 o 
¨ 
o 
Bei den ersten getypten Programmiersprachen (z.B. Pascal): ⇒  Programmierer musste dieselbe Datenstruktur für jeden Datentyp, der unterstützt wurde, definieren. ⇒  Eine Liste von Zahlen, eine Liste von Zeichen und eine Liste von Datumsangaben wird (im Grunde) auf dieselbe Weise programmiert. Die Algorithmen zum Einfügen, Suchen und Löschen laufen stets gleich ab. Wünschenswert: die ImplemenSerung der Liste unabhängig von diesen Typen vorzunehmen. Erster Verbesserungsansatz in Java durch Vererbungsbeziehung ⇒  Ziel nur teilweise erreicht à keine Unterstützung bezüglich der Typüberprüfungen zur Compilezeit. Generische Typen in Java 12 o 
o 
o 
Ein generischer Typ à Erzeugung von Datentypen, die von den zu Grunde liegenden Typen abstrahieren. CharakterisSsch für generischen Programmierung à die Algorithmen werden nicht für einen besSmmten Datentyp geschrieben. Sie stellen nur besSmmte Anforderungen an die Typen. Generische Typen in der InformaSk: Datentypen mit der Möglichkeit zur Angabe von Typparametern. Generische Programmierung – Was ist das? 13 o 
o 
o 
o 
o 
o 
In C++: Templates, in Java: Generics. Der Begriff steht synonym für „parametrisierte Typen“. Idee: Es werden zusätzliche Variablen für Typen, sogenannte Typ-­‐Variablen eingeführt. Diese Typ-­‐Variablen repräsenSeren zum Zeitpunkt der ImplemenSerung unbekannte Typen. Erst bei der Verwendung der Klassen, Schninstellen und Methoden werden diese Typ-­‐Variablen durch konkrete Typen ersetzt. Damit kann typsichere Programmierung gewährleistet werden. Generics – wie funkSonieren sie? 14 Formaler Typparameter
Kein Cast mehr nötig!
Fehlermeldung zur Compilezeit!
The method push(String) in the type GenericSimpleStack<String> is
not applicable for the arguments (int)
Generics: Eigenschaoen 15 o 
Generische Typen erlauben vom konkreten Typ zu abstrahieren. ¤ 
¤ 
¤ 
¤ 
¤ 
mit der Möglichkeit zur Angabe von Typparametern , d.h. Typ einer Variable, eines Parameters, eines Rückgabewertes etc. ist selber ein Variable (type variable). Klassen und Methoden haben, quasi als Schablone, einen zusätzlichen Typ-­‐Parameter (type parameter), diese setzen dann die Typvariablen. Der Programmierer setzt die Typ-­‐Parameter , so wie er es braucht und parametriert damit die Klassen bzw. Methoden. Klassen mit Typ-­‐Parametern à generische Typen (generic types) bzw. generische Klassen (generic classes) Methoden mit Typ-­‐Parametern à generische Methoden (generic methods) ⇒ 
⇒ 
⇒ 
Typprüfung bereits zur Compilezeit à Typsicherheit. Verbessert Lesbarkeit und Robustheit der Programme. (Enqernte) Ähnlichkeiten zu Templates in C++ 15
Generics DefiniSon 16 ¨ 
¨ 
¨ 
¨ 
DefiniSon von Typparametern: in spitzen Klammern hinter dem Klassen-­‐ oder Interfacenamen. Typparameter innerhalb der Klasse bzw. des Interfaces verwendbar wie ein Typ. Typparameter: keine elementaren Datentypen, nur Unterklassen von Object zulässig. Empfehlung für NamenskonvenSon ¤ 
¤ 
Großbuchstabe E in Containerklassen Verwendung
des Typparameters in
Methoden
Formaler
Typparameter
Verwendung des
Typparameters bei
Attributen
Generics – Begriffe und Beispiele 17 Name
Beispiel
Generischer Typ (generic type)
List<E>
Formaler Typ-Parameter (formal type parameter)
E
Parametrisierter Typ (parametrized type)
List<String>
Aktueller Typ-Parameter (actual type parameter)
String
Ungebundener Wildcard-Typ (unbounded wildcard type)
List<?>
Raw Type
List
Gebundener Typ-Parameter (bounded type parameter)
<E extends Number>
Rekursiv gebundener Typ-Parameter (recursive bounded type)
<E extends Comparable<E>>
Gebundener Wildcard-Typ (bounded wildcard type)
List<? extends Number>
Generische Methode (generic method)
static <E> List<E> asList(E[] a)
Type Token
String.class
Generics – Realisierung in Java 18 o 
o 
Generics sind ein Compile-­‐Zeit-­‐Konstrukt, d.h. die Generics exisSeren nur zur Compile-­‐Zeit. Die Typ-­‐InformaSonen des generischen Typs werden vom Compiler enqernt (Typlöschung / type erasure). ¤ 
¤ 
¤ 
o 
o 
Typ-­‐Parameter ist zur Laufzeit vom Typ Object. Typ-­‐Parameter kann nicht in staSschen Variablen oder Methoden verwendet werden. Typ-­‐Parameter kann nicht verwendet werden, um Objekte zu erzeugen. Es gibt zur Laufzeit nur eine einzige Klasse pro generischem Typ (Raw type) – sog. homogene Übersetzung. In C++ wird für jede Ausprägung eines Templates der Code für eine Klasse bzw. FunkSon generiert und compiliert; es gibt also pro generischem Typ in C++ eine Klasse – sog. heterogene Übersetzung. Raw-­‐Type einer generischen Klasse 19 Generische Klasse mit Typvariablen. Compilezeitkonstrukt
Vom Compiler aus der generischen Klasse generierter sog. Raw-­‐Type. Laufzeitkonstrukt
Typ-­‐Variablen und parametrisierte Typen -­‐ KompaSbilität 20 Gegeben: class C<E> { … }
class A { … }
class B extends A { … }
Dann geltenden folgende KompaSbilitätsbedingungen: C<A> # C<B>
Vererbungsbeziehung kann nicht auf generische Typen übertragen werden. Erklärung: s.u.
C<B> # C<A>
C<Object> # C<A>
C<Object> # C<B>
C ← C<A>
Der raw-­‐Type ist mit jedem generischen Typ kompaSbel
C ← C<B>
Legende:
Object ← E
← kompatibel
E # Object
#
nicht kompatibel
Parametrisierte Typen : parametrisierter Typ als aktueller Typ-­‐Parameter 21 Gegeben: class C<E> { … }
class D<T> { … }
Verwendung der Klasse C:
C<D<A>> c = new C<D<A>>();
oder
C<D<D<D<D<D<A>>>>>> c = new C<D<D<D<D<D<A>>>>>>();
22 Parametrisierte Typen : parametrisierter Typ als aktueller Typ Gegeben: class A { … }
class B<E> { … }
Formaler Typparameter
DefiniSon der Klasse C: Attribut-Typ ist eine Typ-Variable
class C<E> {
B<E> b;
void set(B<E> b) { this.b = b; }
B<E> get() { return this.b; }
}
Verwendung der Klasse C: C<A> c = new C<A> ();
c.set(new B<A> ());
B<A> b = c.get();
Parametrisierter Typ
23 Typ-­‐Variablen mit Einschränkungen: gebundender Parametertyp Die Typ-­‐Variable E der Klasse ist eingeschränkt auf CA-­‐kompaSble Typen. Vom Compiler generierter Raw-Type.
Sinn von Einschränkungen 24 o 
Einschränkung von Typen ist ein spezifischer Kontrakt: ¤ 
¤ 
Es wird garanSert, dass der aktuelle Typ-­‐Parameter einer solchen Klasse kompaSbel zu der definierten Einschränkung ist. Über eine Referenz vom Typ der Typ-­‐Variablen können typsicher alle für den einschränkenden Typ definierten Methoden aufgerufen werden. Ohne Einschränkung auf CA könnte man auf eine EReferenz nur die Methoden der Klasse Object aufrufen.
Methode m der Klasse CA kann aufgerufen werden.
Generische Klassen und Vererbung 25 o 
o 
Generische Klassen können Subklassen haben. Die Subklasse ist ¤ 
¤ 
Entweder selbst wieder generisch, oder Die Subklasse legt den Typ-­‐Parameter fest und ist damit nicht generisch. 26 Generische Klassen und Vererbung – Beispiel „Generische Subklasse“ Klasse B ist selbst
generisch.
27 Generische Klassen und Vererbung – Beispiel „Nicht-­‐
Generische Subklasse“ Klasse C ist nicht generisch –
legt den Typ von A fest (bindet
den formalen Parameter E an
Double)
Generics und Subtyping? 28 Subtyping nicht
erlaubt!!!
Fehler?!
Ist eine Liste von Strings auch eine Liste von Objekten?
... sonst ginge das:
Versuch einem String ein Objekt zuzuweisen!
Invarianz 29 o 
Generics sind invariant ¤ 
¤ 
Die Ableitungsbeziehung zwischen Typargumenten überträgt sich nicht auf generische Klassen à Generische Typen von Subtypen sind selber keine Subtypen (invariante Typen, keine Kovarianz). Es gibt daher auch keine Polymorphie zwischen verschiedenen Ausprägungen desselben generischen Typs. Typsichere Behandlung von verschiedenen (generischen) CollecSons 30 Beispiel ohne Generics
Naheliegender Ansatz mit Generics
Nur Collections, die mit Object
parametrisiert sind, können an die
Methode übergeben werden!
for-eachSchleife
Hoffnung:
• 
durch die Definition des Methodenparameters als Collection<Object> wird auch Collection<String> akzeptiert.
Realität:
• 
Das ist aber nicht so.
Generics sind invariant
• 
Beide Typen stehen in keinerlei Beziehung zueinander.
• 
Sie sind zwar durch Parameterisierung aus demselben generischen Typ entstanden, aber sie sind in keiner
Weise kompatibel zueinander.
Wildcards 31 o 
o 
Wildcards <?> à bewusstes „Vergessen“ der TypinformaSonen bzw. zeigen an, dass jede beliebige Ausprägung eines generischen Typs ( = “alle Referenztypen“) möglich ist. <?> steht für unbekannter Typ nicht für Objekt (sonst gälten ja die Einschränkungen von oben). o 
Damit ist es möglich verschiedene Unterklassen zusammenzuführen. o 
<?> ist die Kurzform von <? extends Object> o 
o 
Bound Wildcards <? extends E> bzw. <? super E> stellen sicher, dass nicht jede beliebige Ausprägung des generischen Typs sondern nur besSmmte möglich sind. Verwendung von Wildcards: ¤ 
¤ 
Nur bei der DeklaraSon von Parametern und Variablen. Bei der Objekterzeugung und der DeklaraSon von generischen Typen können sie nicht verwendet werden. Es gibt also keine Objekte eines Wildcard-­‐Typs. Wildcards: Beispiel 1 32 Box <?> bo3 = new Box <?>();
Bei der Erzeugung muss der aktuelle
Typparameter angegeben werden, er darf
nicht unbekannt bleiben.
Der parametrisierteTyp von
bo1 ist Number.
Der parametrisierteTyp Typ
von bo2 ist
unbekannt.
Wildcards: Beispiel 2 33 •  Neuer Ansatz mit Generics
„Collection of unknown“
Wildcard
Akzeptiert alle Arten von Collections!
Gebundene / Upper Bound Wildcards 34 Auf einem Typ, der mit einem
nach oben beschränkten Wildcard
parametrisiert ist, dürfen keine
Methoden aufgerufen werden, die
den Typparameter als
Methodenparameter haben.
Nur Subtypen von Number sind als unbekannter
generischer Typ zulässig!
Die Nutzung der Box<Number> ist eingeschränkt:
setValue() funktioniert nur über die konkrete
Box<Integer> (also bI), aber nicht über die allgemeine
Box<Number>.
Der Zugriff ist unproblematisch.
Lösung à später
Gebundene Wildcards bei Parametern 35 Wildcard für alle Klassen die
Unterklasse von Shape sind
Gebundene / Lower Bound Wildcards 36 Nur Supertypen von A1 sind als
unbekannter generischer Typ zulässig!
Mischen von upper und lower bound Wildcards 37 Mit der upper bound Wildcard (dem maximalen Typ)
funktioniert das Lesen der Daten, aber nicht das
Hinzufügen.
Lesen von Daten aus generischem Typ è upper bound
Wildcards
Nur Supertypen von Number sind als
unbekannter generischer Typ zulässig!
Mit der lower bound Wildcard (dem minimalen Typ)
funktioniert nun das Hinzufügen – aber nicht mehr das
Lesen der Daten.
Hinzufügen von Objekten zu generischem Typ è lower
bound Wildcards
Zusammenfassendes Beispiel 38 Machen wir am Rechner.
Generische Methoden 39 Eine Klasse kann ganz normal ohne Generics deklariert werden, aber mit Methoden, die die Typen generisch vorschreiben. o  Sowohl Klassenmethoden als auch Objektmethoden können als generische Methoden deklariert werden, z.B. static <E> Stack<E> combine (Stack<E> p). Angabe von <E> beim
Klassennamen entfällt
und verschiebt sich
auf die Deklaration der
Methode.
Rückgabetyp: Objekt
der generischen
Klasse Stack<E>
Parametertyp: Objekt
der generischen
Klasse Stack<E>
o 
o 
o 
Im Gegensatz zu Klassen muss der Verwender den Typ-­‐Parameter nicht explizit setzen, der Compiler leitet ihn aus den Typen des Aufrufs ab (type inference). In seltenen Fällen muss man den Typ für die Methode explizit angeben. Interessant ist dies für USlity-­‐Klassen, die nur staSsche FunkSonen anbieten, aber selbst nicht als Objekt vorliegen. Generische Methoden: Beispiel 1 40 class MyStack<E> implements Stack<E>
2 Integer Stacks werden zu einem Stack zusammengefasst.
Generische Methoden: Beispiel 2 41 Fehler zur Compilezeit!
Typparameter
Verwendung des Typparameters
Generische Methoden mit gebundenen Parametern 42 o 
o 
o 
Typ-­‐Parameter können -­‐ analog zu Wildcards – auf besSmmte Klassen beschränkt werden. <T extends C> -­‐ schränkt T auf C und Subklassen von C ein. <T super C> -­‐ schränkt T auf C und Superklassen von C ein. Generische Methoden: Beispiel 3 43 T muss von Klasse A abgeleitet sein
T muss von den Interfaces I1 und I2 abgeleitet sein
T muss von Klasse A und den Interfaces I1 und I2 abgeleitet sein
Generics mit mehreren Typparametern 44 Generics und Arrays 45 o 
o 
o 
o 
o 
o 
o 
Generics und Arrays unterscheiden sich grundsätzlich und harmonieren im allgemeinen schlecht. Arrays sind kovariant (covariant) Generics sind invariant (invariant) Arrays prüfen ihren Typ zur Laufzeit (reified) Generics sind ein Compilezeitkonstrukt, prüfen ihren Typ deshalb zur Compilezeit (type erasure). Die Typlöschung ist der Grund dafür, das Arrays nicht so umgesetzt werden können, wie man es sich naiv vorstellt. Es ist nicht möglich ein Array zu erstellen aus ¤ 
¤ 
¤ 
einem generischen Typ (List<E>[]) einem parametrierten Typ (List<String>[]) einem Typ-­‐Parameter (E[]) Kovarianz, Kontravarianz, Invarianz 46 Vererbung vom Typ des
Methodenparameters
bzw. Rückgabewerts
Typhierarchie des
Methodenparameters
ist entgegen der
Vererbungshierarchie
von ClassA und
ClassB
Typhierarchie des
Rückgabewertes der
Methode ist mit der
Vererbungshierarchie
von ClassA und ClassB
Typhierachie des
Methodenparameters
bleibt unverändert
Quelle: Wikipedia
RunSme-­‐Klassen 47 o 
Was gibt das folgende Programm aus? Gibt „true“ aus, weil alle Instanzen einer generischen Klasse
dieselbe Runtime-Klasse haben
Anwendungsbeispiel: Queue 48 Anwendungsbeispiel: List 49 Anwendungsbeispiel: Node 50 Einsatz von Generics 51 o 
Allgemein ¤ 
¤ 
o 
o 
o 
Generics sind eine Form von Polymorphie. Diese ist immer dann sinnvoll, wenn der Code wiederverwendbar sein soll. Generics kontra Polymorphie durch Subtyping: ¤ 
Generics für die Typsicherheit. ¤ 
Vorteil gegenüber Casts durch frühere Fehlererkennung. Der Entwickler wird nicht gezwungen Generics zu verwenden. Eine unchecked Warnung weist darauf hin, dass die Typsicherheit nicht gewährleistet ist. Zusammenfassung 52 o 
Generics erlauben typsicher zu programmieren ¤ 
¤ 
Typprüfungen erfolgen bereits zur Compilezeit nicht erst zur Laufzeit. D.h. Fehler treten an den Stellen auf, wo sie verursacht werden. Generics helfen dabei Programme robuster und weniger fehleranfällig zu machen. n 
n 
Das Wegfallen von Casts macht den Code übersichtlicher. Bedingungen lassen sich besser und einfacher ausdrücken. o 
Generische Klassen können leicht wiederverwendet werden. o 
In Bezug auf Arrays ist das Konzept noch nicht ausgereio. Generics – Erweiterungen in Java 7 53 Mit Java 7 ist die Typ-­‐Inferenz für Generics deutlich verbessert worden: o  In den meisten SituaSonen muss man den Typ-­‐Parameter nur noch bei der DeklaraSon setzen. o  Bei der Objekterzeugung kann die Angabe des Typ-­‐
Parameters durch den diamond <> ersetzt werden. Beispiele: List<String> list = new ArrayList<>();!
Map<Integer, String> map = new HashMap<>();!
Set<?> set = new HashSet<>();!
Weitere Infos zu Generics 54 o 
o 
hnp://java.sun.com/j2se/1.5/pdf/generics-­‐tutorial.pdf hnp://angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.pdf 
Herunterladen