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 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
Formaler Typ-Parameter (formal type parameter)
E
Parametrisierter Typ (parametrized type)
List
Aktueller Typ-Parameter (actual type parameter)
String
Ungebundener Wildcard-Typ (unbounded wildcard type)
List
Raw Type
List
Gebundener Typ-Parameter (bounded type parameter)
Rekursiv gebundener Typ-Parameter (recursive bounded type)
Gebundener Wildcard-Typ (bounded wildcard type)
List à 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 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 ist eingeschränkt: setValue() funktioniert nur über die konkrete Box (also bI), aber nicht über die allgemeine Box. 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 Stack combine (Stack p). Angabe von beim Klassennamen entfällt und verschiebt sich auf die Deklaration der Methode.
Rückgabetyp: Objekt der generischen Klasse Stack
Parametertyp: Objekt der generischen Klasse Stack
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 implements Stack
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. -‐ schränkt T auf C und Subklassen von C ein. -‐ 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[]) einem parametrierten Typ (List[]) 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 list = new ArrayList();! Map 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