Generisches Programmieren

Werbung
Generisches Programmieren
Generisches Programmieren
24.11.11
Uli Dormann
1
Inhaltsverzeichnis
Motivation
Ansatz
Sprachen mit generischer Programmierung
Generische Klassen
Namenskonvention
Prüfung Typsicherheit
Generische Klassen, Interfaces
Schachteln
Der Diamantoperator
Typebounds
Wildcard
Vererbung
Übersetzen generischer Klassen
Type Erasure
Generics und Arrays
Brückenmethoden
Grenzen generischer Typen
Polymorphie
Abschluss
2
Motivation (1)
Beispiel Tasche
Für jede Möglichkeit eine eigene Klasse
String
Integer
public class Pocket
public class Pocket
{
{
private String value;
private int value;
public Pocket() {}
public Pocket() {}
public Pocket( String value ) { this.value = value; }
public Pocket( int value ) { this.value = value; }
public void set( String value ) { this.value = value; }
public void set( int value ) { this.value = value; }
public String get() { return value; }
public int get() { return value; }
public boolean isEmpty() { return value == null; }
public boolean isEmpty() { return value == null; }
public void empty() { value = null; }
public void empty() { value = null; }
3
Motivation (2)
Beispiel: Tasche
Objekt
public class Pocket
{
private Object value;
public Pocket() {}
public Pocket( Object value ) { this.value = value; }
public void set( Object value ) { this.value = value; }
public Object get() { return value; }
public boolean isEmpty() { return value == null; }
public void empty() { value = null; }
}
Unpraktisch und nicht Typsicher!
4
Ansatz
Generischer Algorithmus
Mechanismus zur Verwendung solcher Algorithmen
In Java „Generics“ mit Version 5
Generics sind Objekte, keine primitiven Datentypen.
Primitive Typen: boolean, char, byte, short, int, float, double
AutoBoxing : Primitiver Datentyp ↔ Wrapper Objekt
5
Sprachen mit generischer Programmierung
Java
NET-Sprachen
ADA
Delphi
HaXe
ABAP
Eiffel
ML
Python
C#
C++
Makros in C (wenn auch nicht dafür vorgesehen)
6
Generische Klassen (1)
Typparameter wird durch Typvariable ersetzt
public class Pocket<T>
{
private T value;
public Pocket() {}
public Pocket( T value ) { this.value = value; }
public void set( T value ) { this.value = value; }
public T get() { return value; }
public boolean isEmpty() { return value != null; }
public void empty() { value = null; }
}
7
Generische Klassen (2)
Typvariablen (fast) wie ein normaler Typ
Mehrere Typvariablen möglich
Beispiel:
public class Pocket<T>
Pocket<Integer> intPocket = new Pocket<Integer>();
Pocket<String> stringPocket = new Pocket<String>();
Begriff
Beispiel
Generische Typen
Pocket<T>
Typvariable
T
Parametrisierter Typ
Pocket<Long>
Typparameter
Long
Originaltyp
Pocket
8
Namenskonvention
Einzelne Großbuchstaben z.B. E wie Element
Keine Wörter
Der Quellcode wird öfter gelesen als geschrieben, deswegen
leserlich halten.
9
Prüfung Typsicherheit
Zwei Instanzen, die die Typen prüfen
- JVM
- Compiler
→ Compiler braucht mehr Informationen
10
Generische Klassen, Interfaces
→ class Pocket <T>
→ interface XXX<T>
11
Schachteln
Typvariable T beschränkt sich nicht auf Klassen- oder
Schnittstellentypen, sondern kann auch wieder ein generischer
Typ sein.
Pocket<Pocket<String>> pocketOfPockets = new Pocket<Pocket<String>>();
pocketOfPockets.set( new Pocket<String>() );
pocketOfPockets.get().set( "Inner Pocket<String>" );
System.out.println( pocketOfPockets.get().get() ); // Inner Pocket<String>
Ohne Generics sähen alle Taschen gleich aus.
12
Der Diamantoperator
Ab Java 7 wird aus:
Pocket<Pocket<String>> pocketOfPockets = new Pocket<Pocket<String>>();
einfach:
Pocket<Pocket<String>> pocketOfPockets = new Pocket<>();
13
Typebounds
bedeutet: Kompatibel zu
Klassen und Interfaces möglich
Kann mehrere geben
class Pocket<T extends A & B>{...
Legt Mindestanforderung fest
Kann die Typvariable enthalten
class Pocket<T extends Comparable<T>> {...
14
Wildcard
Bewusstes vergessen der Typinformation (Joker ;)
Pocket<Number> b;
Pocket<Integer> bI = new Pocket <Integer>();
Pocket<Double> bD = new Pocket <Double>();
b = bI; // Nein
Pocket<? extends Number> b;
Pocket<Integer> bI = new Pocket<Integer>();
Pocket<Double> bD = new Pocket <Double>();
Pocket<String> bS = new Pocket <String>();
b = bI; // Ja
b = bS; // Nein
15
Vererbung (1)
Invarianz
Verschiedene generische Typen sind zueinander inkompatibel,
unabhängig von der Kompatibilität ihrer Typargumente
Covarianz
Zu Wildcard-Typen mit Upper-Typebound (C<? extends B>)
sind alle generischen Typen kompatibel, deren Typargument
zu B kompatibel ist
16
Vererbung (2)
Varianzenübersicht
Typ
Kompatible
Typargumente
Invarianz
C<T>
T
Bivarianz
C<?>
Alle
Covarianz
C<? extends B>
B und abgeleritete
Typen
Contravarianz
C<? super B>
B und Basistypen
17
Übersetzen generischer Klassen
Ansatz:
Generische Datentypen werden in Java ausschließlich vom
Compiler verarbeitet
Laufzeitsystem weiß nichts von generischen Typen
Übersetzung:
Generischer Code wird in nicht generischen Code
umgewandelt
Aus jeder generischen Klasse wird eine nicht-generische
Klasse generiert und in eine .class-Datei übersetzt
→ In C++ wird dagegen jede Instanziierung einer Klasse mit Typargumenten
getrennt übersetzt -> dadurch langsameres Übersetzen und größere Kompilate aber
bessere Optimierungsmöglichkeiten und weniger Einschränkungen
18
Type Erasure (1)
Typ-Variablen in spitzen Klammern löschen
Vorkommen von Typvariablen mit einem oder mehreren Typebounds durch den
einzigen bzw. ersten Typebound ersetzen
Vorkommen von Typvariablen ohne Typebounds durch Object ersetzen
Die Typ-Korrektheit statisch prüfen (d.h. zum Übersetzungszeitpunkt)
Typargumente müssen allen Typebounds genügen
Generische Typen müssen auch untereinander korrekt verwendet werden,
insbesondere bei Wildcard-Typen
Typargumente, einschließlich Wildcards, in spitzen Klammern gelöscht
Typecasts eingeschoben, wo der Wert eines Typarguments benutzt wird
19
Type erasure (2)
Beispiel: Ohne Typebounds
Generische Klasse
public class Pocket<T>
{
private T value;
public Pocket() {}
public Pocket( T value )
{ this.value = value; }
Nach Type-Erasure
public class Pocket
{
private Object value;
public Pocket() {}
public Pocket( Object value )
{ this.value = value; }
public void set( T value )
{ this.value = value; }
public void set( Object value )
{ this.value = value; }
public T get() { return value; }
public boolean isEmpty()
{ return value != null; }
public Object get() { return value; }
public boolean isEmpty()
{ return value != null; }
public void empty() { value = null; }
}
public void empty() { value = null; }
}
20
Generics und Arrays (1)
Nicht möglich:
class TwoBox<T>
{
private T[ ] array = new T[ 2 ];
T[ ] getArray() { return array; }
}
// Fehler Cannot create a generic array of T
Grund:
class TwoBox
{
Object[ ] array = new Object[ 2 ];
Object[ ] getArray() { return array; }
}
// (1)
21
Generics und Arrays (2)
Parametriert:
TwoBox<String> twoStrings = new TwoBox<String>();
String[ ] stringArray = twoStrings.getArray();
Compiler generiert:
TwoBox twoStrings = new TwoBox();
String[ ] stringArray = (String[ ]) twoStrings.getArray(); // (2)
Lösung:
Reflection
T[ ] array = (T[]) Array.newInstance( clazz, 2 );
22
Brückenmethoden
Schnittstelle Comparable:
public interface Comparable<T> { public int compareTo( T o ); }
Klasse Integer:
public final class Integer extends Number implements Comparable<Integer>
Neu angelegte Brückenmethode:
public int compareTo( Object anotherInteger )
{
return compareTo( (Integer) anotherInteger );
}
23
Grenzen generischer Typen (1)
Primitive Typargumente
Node<int> ni = new Node<int>(23); // Fehler!
Aber:
Node<Integer> ni = new Node<Integer>(23); // Autoboxing
Statische Elemente
class Broken<T> { static T data; } // Fehler!
Grund: Alle Klassen Broken<T> teilen sich das Klassenattribut data. Welchen Typ soll es
haben?
Dynamische Typprüfung
class Pocket<T> { boolean isCompatible(Object o) { return o instanceof T; } } //
Type-Erasure: o instanceof T → o instanceof Object
Fehler!
24
Grenzen generischer Typen (2)
Typecasts
Class Node<T> { T info; void setInfo(Object o) { info = (T) o; } } // Sinnlos
Type-Erasure: (T) o → (Object) o
Compiler erzeugt: „warning: unchecked cast of type T“
Konstruktoraufrufe
class Node<T> { T info; Node() { info = new T(); } } // Fehler
Woher soll Java wissen, dass T einen Standard-Konstruktor hat?
Beispiel:
Node<Integer> ni = new Node<Integer>();
Ausweg:
class Node<T> { T info; Node(T i) { info = i; } }
Node<Integer> ni = new Node<Integer>(23);
Generische Basistypen
import java.util.Date;
class Timestamped<T> extends T { Date timestamp = new Date(); } // Fehler
Generische Klasse muss Konstruktor der Basisklasse aufrufen können. Dieser kann hier
aber nicht zur Kompilierzeit ermittelt werden!
25
Grenzen generischer Typen (3)
Exceptions
class UniversalException<T> extends Exception { T reason; } // Fehler
Generische Typen können nicht für Exceptions verwendet werden, da das Fangen mit catch
auf dem Ermitteln des Typs des geworfenen Objekts basiert. Dieser geht aber bei der
Type-Erasure verloren.
Compiler erzeugt: „a generic class may not extend java.lang.Throwable“
26
Polymorphie und Generics
Definition
Polymorphe Methoden sind unabhängig von generischen
Typen
Sie können auch in nicht-generischen Klassen definiert
werden
27
Abschluss
Fragen?
Danke für die Aufmerksamkeit!
28
Quellen
Java ist auch eine Insel
Uni Bremen Unterlagen zu PI-1
Wikipedia
Www.dpunkt.de
Uni Hamburg Unterlagen SE-1
29
Herunterladen