Generische Datenstrukturen

Werbung
Generische Datenstrukturen
Prof. Dr. rer. nat. habil. Uwe Aßmann
Lehrstuhl Softwaretechnologie
Fakultät für Informatik
TU Dresden
Softwaretechnologie, © Prof. Uwe Aßmann
1
2 Trends in der Softwareentwicklung
►
Rapid Application Development (RAD)
■
■
Schneller viel Code schreiben
Typisierung weglassen
Zeit
pike
♦ Bei den Assoziationen
♦ Beim Programmieren gegen Schnittstellen
■
►
Mächtige Operationen, die schnell zu schreiben sind
Java
Java
1.5
Safe Application Development (SAD)
■
■
■
Zuverlässigkeit
Guten Code schreiben
Typisierung, damit der Übersetzer viele Fehler entdeckt (statische Typisierung)
Mehr Entwurfswissen aus dem Entwurf in die Implementierung übertragen
♦ Typisierung der Assoziationen
■
Aus der Definition einer Datenstruktur können Bedingungen für ihre Anwendung
abgeleitet werden
Prof. U. Aßmann, Softwaretechnologie
2
Elemente einer Hierarchie
Formular
Bestellung
Lieferschein
Rechnung
Prof. U. Aßmann, Softwaretechnologie
3
Problem 1 ungetypter Schnittstellen: Laufzeitfehler
►
►
Bei der Konstruktion von Collections werden oft Fehler programmiert, die bei
der Dekonstruktion zu Laufzeitfehlern führen
Kann in Java < 1.4 nicht durch den Übersetzer entdeckt werden
List listOfRechnung = new ArrayList();
Rechnung rechnung = new Rechnung();
listOfRechnung.add(rechnung);
Bestellung best = new Bestellung();
listOfRechnung.add(best);
Programmierfehler!
for (int i = 0; i < listOfRechnung.size(); i++) {
((Rechnung)rechnung) = listOfRechnung.get(i);
}
Laufzeitfehler!!
Prof. U. Aßmann, Softwaretechnologie
4
Problem 2 ungetypter Schnittstellen: Unnötige Casts
►
Bei der Dekonstruktion von Collections müssen unnötig Casts spezifiziert
werden
List listOfRechnung = new ArrayList();
Rechnung rechnung = new Rechnung();
listOfRechnung.add(rechnung);
Rechnung rechnung2 = new Rechnung();
listOfRechnung.add(rechnung2);
Diesmal ok
for (int i = 0; i < listOfRechnung.size(); i++) {
((Rechnung)rechnung) = listOfRechnung.get(i);
}
Cast nötig, obwohl
alles Rechnungen
Prof. U. Aßmann, Softwaretechnologie
5
Abhilfe: Generische Klassen
Eine generische Klasse ist eine Klassenschablone, die mit einem
Typparameter P versehen ist.
►
In UML
►
In Java
■
Sprachregelung: “Array of T”
P
Container
content
P
class Container<P> {
P content[];
}
Prof. U. Aßmann, Softwaretechnologie
6
Generische Datentypen in der Collection-Hierarchie
►
Die neue, generische Hierarchie (seit Java 1.5)
E
Collection
E
List
E
Set
Queue
E
SortedSet
E
Blocking
Queue
K,V
Map
E
K,V
SortedMap
Prof. U. Aßmann, Softwaretechnologie
7
Instanz der Generischen Hierarchie
E
Collection
E
E
List
Collection
<Formular>
Set
<<instantiates>
>
Queue
E
SortedSet
List
<Formular>
Set
<Formular>
SortedSet
<Formular>
Queue
<Formular>
Blocking
Queue
<Formular>
E
Map
<Nr,Formular>
Blocking
Queue
►
K,V
Map
K,V
E
SortedMap
Darf man Rechnungen,
Bestellungen und
Lieferscheine in diese
Collections stecken?
Ja.
SortedMap
<Nr,Formular>
Prof. U. Aßmann, Softwaretechnologie
8
Probleme gelöst
►
►
Bei der Konstruktion von Collections werden jetzt Äpfel werden von Birnen
unterschieden
Casts sind nicht nötig, der Übersetzer kennt den feineren Typ
List<Rechnung> listOfRechnung = new ArrayList<Rechnung>();
Rechnung rechnung = new Rechnung();
listOfRechnung.add(rechnung);
Bestellung best = new Bestellung();
Compilerfehler
listOfRechnung.add(best);
for (int i = 0; i < listOfRechnung.size(); i++) {
rechnung = listOfRechnung.get(i);
}
Kein Cast mehr nötig
Prof. U. Aßmann, Softwaretechnologie
9
Generizität funktioniert auch geschachtelt
// Das Archiv fasst alle Rechnungen aller bisherigen Jahrgänge zusammen
List<List<Rechnung>> archiv = new ArrayList<List<Rechnung>>();
// listOfRechnung fasst die Rechnungen des aktuellen Jahres zusammen
List<Rechnung> listOfRechnung = new ArrayList<Rechnung>();
archiv.add(listOfRechnung);
Rechnung rechnung = new Rechnung();
archiv.getIndex(0).add(rechnung);
Bestellung best = new Bestellung();
archiv.getIndex(0).add(best);
funktioniert
ÜbersetzungsFehler
for (int jahr = 0; jahr < archiv.size(); jahr++) {
listOfRechnung = archiv.getIndex(jahr);
for (int i = 0; i < listOfRechnung.size(); i++) {
rechnung = listOfRechnung.getIndex(i);
}
}
Prof. U. Aßmann, Softwaretechnologie
10
Benutzung von getypten und ungetypten Schnittstellen
►
.. ist in Java 1.5 ohne Probleme nebeneinander möglich
// Das Archiv fasst alle Rechnungen aller bisherigen Jahrgänge zusammen
List<List<Rechnung>> archiv = new ArrayList<List<Rechnung>>();
// listOfRechnung fasst die Rechnungen des aktuellen Jahres zusammen
List listOfRechnung = new ArrayList();
archiv.add(listOfRechnung);
Rechnung rechnung = new Rechnung();
archiv.getIndex(0).add(rechnung);
Bestellung best = new Bestellung();
archiv.getIndex(0).add(best);
funktioniert
Übersetzt auch,
for (int jahr = 0; jahr < archiv.size(); jahr++) { aber Laufzeitfehler
listOfRechnung = archiv.getIndex(jahr);
beim Cast...
}
for (int i = 0; i < listOfRechnung.size(); i++) {
rechnung = (Rechnung)listOfRechnung.getIndex(i);
}
Prof. U. Aßmann, Softwaretechnologie
11
Typschranken generischer Parameter (type bounds)
►
Beispiel: Comparable<E> als Return-typ in der Collections-Klasse sichert zu,
dass die Methode compareTo() existiert
class Collections {
/** minimum function for a Collection. Return value is typed
* with a generic type with a type bound */
public static <E extends Comparable<E>> min(Collection<E> ce) {
Iterator<E> iter = ce.iterator();
E curMin = iter.next;
if (curMin == null) return curMin;
for (E element = curMin;
iter.hasNext(), element = iter.next) {
if (element.compareTo(curMin) < 0) {
curMin = element;
}
}
return curMin;
}
Prof. U. Aßmann, Softwaretechnologie
12
Generische Methoden als Funktionale Objekte
Ein Funktionalobjekt ist ein Objekt, das eine Funktion darstellt (reifiziert).
►
Funktionalobjekte können Berechnungen kapseln und später ausführen
(laziness)
■
Es gibt eine Funktion in der Klasse des Funktionalobjektes, dass die Berechnung
ausführt
♦ Generischer Name, z.B. execute() oder doIt()
►
Zur Laufzeit kann man
■
■
■
das Funktionalobjekt mit Parametern versehen
Herumreichen
Und zum Schluss ausführen
Prof. U. Aßmann, Softwaretechnologie
13
Generische Methoden als Funktionale Objekte
►
Anwendung: Akkumulatoren und andere generische Listenoperationen
P
BinOp
Collection
<P>
B
Functional
E
Accumulate
// A functional object that adds, once invoked
interface BinOp<P> { P execute(P p1, P p2) }
// an interface for a collection of binary operation on
// collections
interface Functional<Collection<P>,B extends BinOp<P>> {
P compute(Collection<P> p);
}
class Accumulate<C,P> implements Functional<C,P> {
P curSum; P element; B binaryOperation;
public P compute(Collection<P> c) {
for (int i = 0; i < c.size(); i++) {
element = c.getIndex(i);
binaryOperation.execute(curSum,p);
}
return curSum;
}
}
Prof. U. Aßmann, Softwaretechnologie
14
Unterschiede zu C++
►
In Java: einmalige Übersetzung des generischen Datentyps
■
■
►
►
Verliert etwas Effizienz, da der Übersetzer alle Typinformation im generierten
Code vergisst und nicht ausnutzt
z.B. sind alle Instanzen mit unboxed objects als boxed objects realisiert
C++ bietet Code-Templates (snippets, fragments) an, mit denen man mehr
parameterisieren kann, z.B. Methoden
In C++ können Templateparameter Variablen umbenennen:
template class C <class T> {
T attribute<T>
}
Templateparameter können Variablen umbenennen
Prof. U. Aßmann, Softwaretechnologie
15
End
Prof. U. Aßmann, Softwaretechnologie
16
Herunterladen