Generics seit Java1.5 SSJ Kapitel 15 Probleme ohne generische Typen Klassen, die mit Objekten verschiedenen Typs arbeiten können class List { Object[ ] data = ; void add(object x) { Object remove() { Probleme Typumwandlungen nötig list.add(3); int x = (Integer)list.remove(); // Boxing kostet Zeit // Typumwandlung und Unboxing kosten Zeit Spezielle Typen IntList, PersonList, führen zu Redundanz Homogenität kann nicht erzwungen werden list.add(3); list.add(new Person()); Person p = (Person) list.remove(); // kann zu Laufzeitfehler führen! Page 1
Generische Klasse List generischer Typ Platzhalter class List<T> { T[ ] data; List(int size) { void add(t x) { T remove() { geht auch für Interfaces Platzhaltertyp T kann wie normaler Typ verwendet werden Benutzung List<Integer> a = new List<Integer>(100); a.add(3); // nur Integer-Parameter erlaubt; hier Boxing int i = a.remove(); // liefert Integer-Wert; kein Cast nötig List<Person> b = new List<Person>(100); b.add(new Person()); // nur Person-Parameter erlaubt Person p = b.remove(); // kein Cast nötig aktueller Typparameter muß ein Referenztyp sein (Integer, Rectangle, ) Vorteile Homogene Datenstruktur mit Compilezeit-Typprüfung Man muß keine Casts schreiben Generizität auch in Ada, Eiffel, C++ (Templates), C# Mehrere Platzhaltertypen Liste mit Schlüsseln und Werten class List <K, V> { K[] keys = ; V[] values = ; void add(k key, V value) { V remove(k key) { Verwendung List<String, Person> list = new List<String, Person>(); list.add("meier", aperson); aperson = list.remove("meier"); List<Integer, String> list = new List<Integer, String>(); list.add(17, "Pizza"); String s = list.remove(17); Page 2
Rohtypen Generischer Typ class List<T> { T[ ] data; List(int size) { void add(t x) { T remove() { wird übersetzt zu Rohtyp class List { Object[] data; List(int size) { void add(object x) { Object remove() { Alle aus List<T> erzeugten Typen werden auf denselben Rohtyp abgebildet List<String> stringlist = new List<String>(10); stringlist.add("abc"); List<Integer> intlist = new List<Integer>(100); intlist.add(15); rufen beide add von List auf und übergeben einen Object-Parameter. Compiler stellt aber sicher, daß intlist nur mit Integer-Werten verwendet wird. Kompatibilitätsbeziehungen List <String> Object List List <Integer> List list = new List<String>(10); Object obj = new List<String>(10); List<Integer> x = new List<String>(10); // ok // ok // verboten Generics Page 3
(Bounded Type Parameters) Annahmen über Platzhaltertypen werden als Basistypen ausgedrückt class SortedList <T extends Comparable> { T[ ] data = ; int nelements = 0; void add(t elem) { int i = nelements - 1; while (i >= 0 && elem.compareto(data[i]) > 0) {data[i+1] = data [ i] ; i--; data [ i+1 ] = elem; nelements++; Erlaubt Operationen auf Elemente von Platzhaltertypen Verwendung SortedList<String> list = new SortedList<String>(); List.add("John"); Parameter muß Comparable unterstützen Mehrere möglich class Hashtable <K extends Comparable, V extends MyClass & Serializable> { void put(k key, V value) { V get(k key) { Verwendung muß Comparable implementieren muß von MyClass abgeleitet sein und Serializable implementieren Hashtable<String, MySubclass> tab = new Hashtable<String, MySubclass>(); tab.put("john", new MySubclass()); Page 4
Generics verboten class List<E, P extends Comparable> { E[ ] data = new E[10]; P[ ] prio = new P[10]; Man darf weder ein Array von Typparametern erzeugen new E[10] new P[10] noch ein Array aus erzeugten Typen Warum? new List<String, Integer>[10] VM weiß nicht, welcher konkrete Typ hinter E und P steckt. erlaubt class List<E, P extends Comparable> { E[ ] data = (E[ ]) new Object[10]; P[ ] prio = (P[ ]) new Comparable[10]; Compiler erzeugt eine Warnung! Warum? Angenommen, List hat folgende Methode E bang() { Object[] a = data; a[0] = crash!"; return data[0]; Dann liefert folgendes eine ClassCastException List<Integer, Integer> list = ; Integer x = list.bang(); Ohne Warnung käme Exception überraschend. Page 5
Generics Generizität und class List <T> extends BaseList<T> { kann auch generische Interfaces implementieren List <T> implements B<T> Von welchen Klassen darf eine generische Klasse erben? von einer gewöhnlichen Klasse class B<X> extends A { von einer konkretisierten class B<X> extends A<String> { generischen Klasse von einer generischen Klasse class B<X> extends A<X> { mit gleichem Platzhalter Page 6
Kompatibilität in Zuweisungen Zuweisung von X< T > an gewöhnliche Oberklasse class A { class B<T> extends A { class C<T,U> extends A { B<T> A C<T,U> A a1 = new B<Integer>(); A a2 = new C<Integer, Float>(); B<Integer> C<Integer,Float> B<Float> C<Float,Integer>.. Zuweisung von X< T > an generische Oberklasse class A<T>{ class B<T> extends A<T> { class C<T,U> extends A <T>{ B<T> A<T> C<T,U> A<Integer> a1 = new B<Integer>(); A<Integer> a2 = new C<Integer, Float>(); A<Short> a3 = new B<Integer>(); erlaubt, wenn korrespondierende Platzhalter durch denselben Typ ersetzt wurden VERBOTEN! Überschreiben von Methoden class List<T> { void add(t x) { Wenn von konkretisierter Klasse geerbt class MyList extends List<Integer> { void add(integer x) { Wenn von generischer Klasse geerbt class MyList<T> extends List<T> { void add(t x) { T wird durch konkreten Typ Integer ersetzt T bleibt als Platzhalter Folgendes geht nicht (man kann keinen Platzhalter erben) class MyList extends List<T> { void add(t x) { VERBOTEN! Page 7
Generics Parametrisierte Typen, bei denen der Typenparameter egal ist List <?> list; List of unknown Kompatibel mit List of any Wozu? gemeinsamer Basistyp für List of any List<Object> ist kein gemeinsamer Basistyp List <Object> list = new List<String>(); //verboten Kompatibilitätsbeziehung A<T> B<T> A<?> a = new A<String>(); A<?> a = new A<Integer>(); A<?> a = new B<String>(); A a = new A<String>(); Object a = new A<String>(); Object A A<?> A<String> B B<?> B<String> Page 8
Bounded Ausgangssituation (Beispiel) class List<T> { void add (T x) { T remove() { Person Student Teacher. List<Student> students = new List<Student>(); students.add(new Student()); students.add(new Student()); List<Teacher> teachers = new List<Teacher>(); teachers.add(new Teacher()); teachers.add(new Teacher()); Wildcard kann auf bestimmte Basistypen eingeschränkt werden List<? extends Person> list; list = teachers; list = students; Person pers = list.remove(); list.add(new Student()); list.add(new Person()); // ok // Compilefehler // Compilefehler list enthält zumindest List<Person> man kann zu list keine neuen Elemente mehr hinzufügen, aber welche entnehmen. Die sind dann vom Typ Person. Kompatibilitätsbeziehung Object List List<?> List<? extends Person> List<Student> List<Teacher> Generics Page 9
Methoden, die mit unterschiedlichen Datentypen arbeiten können static <T> void copy (T[] src, List<T> dst) { for (T elem: src) dst.add(elem); Benutzung Integer[] a = {3, 7, 2, 5, 3; List<Integer> list = new List<Integer>(); copy(a, list); // list == {3, 7, 2, 5, 3 String[] s = {"one", "two", "three"; List<String> list = new List<String>(); copy(s, list); // list == {"one", "two", "three" kopiert ein Array in eine Liste (Elementtyp beliebig) Typ von T wird automatisch aus den aktuellen Parametertypen abgeleitet Unterschied zu void copy (Object[] src, ObjList dst) { for (Object elem: src) dst.add(elem); Hier keine statische Typsicherheit: Compiler garantiert nicht, daß Array und ObjList denselben Elementtyp haben Generics Page 10
Laufzeit-Typprüfung List<String> und List<Integer> werden zur Laufzeit wie List behandelt. VM kennt den Typparameter nicht! Laufzeitprüfungen funktionieren bei Generics nicht oder nur eingeschränkt List<String> list = new List<String>(); Object obj = list; if (obj instanceof List<String>) list = (List<String>) obj; Compilefehler: VM kann nicht prüfen, ob obj vom Typ List<String> ist Warnung: VM kann nicht garantieren, daß obj vom Typ List<String> ist Page 11