pdf

Werbung
Literatur
[6-1]
Nowak, Johannes: Fortgeschrittene Programmierung mit Java 5. dpunkt,
2005
[6-2]
Mössenböck, Hanspeter: Sprechen Sie Java? dpunkt, 3. Auflage, 2005
[6-3]
Fischer, Gregor; von Gudenberg, Jürgen W.: Programmieren in Java
1.5. Springer, 2005
[6-4]
Grude, Ulrich: Java ist eine Sprache. Vieweg, 2005
Komponenten – WS 2014/15 – Teil 5/Generics
2
Idee der parametrisierten Datentypen I
• Parameter sind eigentlich Werte entsprechend einem deklarierten
Typ. Dies ist der Normalfall in Java.
• Was ist aber nun, wenn ein allgemeiner Typ mehrere Untertypen
besitzen kann, die zusammen jeweils Varianten bilden?
• In Java ist dies bis Java 1.4 nur möglich, wenn die Varianten
einen gemeinsamen Vorfahren innerhalb der Vererbungshierarchie
haben. Die Oberklasse ist der allgemeine Typ, während alle davon
abgeleiteten Unterklassen Varianten sind.
• Wenn nun ein Parameter einen Typ als Wert hat und dieser
Parameter bei der Erzeugung benutzt wird, um eine
"Spezialisierung" zu realisieren, wird von einem parametrisierten
Datentyp gesprochen.
• Dies geht erst ab Java-Version 1.5. In Java wird so ein Typ
generisch genannt (Generics).
3
Komponenten – WS 2014/15 – Teil 5/Generics
Idee der parametrisierten Datentypen II
Ein Beispiel:
(Freistil)
Class Queue(ElemType) {
ElemType content[10];
int in:=0, out:= 0;
… … …
proc enqueue(ElemType elem) {
… … …
}
func ElemType dequeue() {
… … …
}
}
Queue(int) intqueue;
Queue(String) strqueue;
intqueue.enqueue(10);
strqueue.enqueue(„Elvira“);
Komponenten – WS 2014/15 – Teil 5/Generics
4
Die Ziele
• Spezialisierung von Typen durch unterschiedlich gesetzte Werte
(Typen) bei der Objektgenerierung.
• Trotzdem sollte der Compiler die Typverträglichkeiten prüfen
können, d.h. kein cast.
• Da Generics später in Java integriert werden, sollte eine
Kompatibilität zur alten Sprache erhalten bleiben.
• Um den Aufwand zu reduzieren, sollten parametrisierte
Datentypen durch den Compiler in "normale" Datentypen
übersetzt werden, so dass die Virtuelle Maschine davon nicht
betroffen wird.
Vorteile davon:
– Alte Programme laufen binär weiter
– Keine neue Virtuelle Maschine
Es wird sich herausstellen, dass die letzten beiden Ziele zu
einem komplexen und schlechten Sprachentwurf führen.
Komponenten – WS 2014/15 – Teil 5/Generics
5
Bisher: Liste von Objekten I
class Queue {
Object[] daten;
...
public void enqueue(Object elem) {...}
public Object dequeue() {...}
...
}
• Um eine Warteschlange für beliebige Objekte zu
implementieren, müssen die Elemente vom Typ Object sein, da
per definitionem alle Objekte von diesem Typ abgeleitet und
daher verträglich sind.
• Diese Art von Klasse wird nun auch Rohtyp bzw. raw type
genannt.
Komponenten – WS 2014/15 – Teil 5/Generics
6
Bisher: Liste von Objekten II
Queue names= new Queue();
names.enqueue("Haribald");
names.enqueue(42);
...
String n= (String)names.dequeue();
String m= (String)names.dequeue(); // Laufzeitfehler
• Solange verabredungsgemäß in die Liste nur Objekte vom
"richtigen" Datentyp - hier String - abgelegt werden, geht alles
mit dem cast gut.
• Auch muss klar sein, dass trotz richtigem Inhalt auch der
richtige cast-Typ verwendet wird.
• Es wäre schön, wenn dies der Compiler prüfen könnte, und
wenn die Verwendung der Casts nicht erforderlich wäre.
Komponenten – WS 2014/15 – Teil 5/Generics
7
Wunsch: Liste von Objekten III
Queue names= new Queue(String);
names.enqueue("Haribald");
names.enqueue(42);
// Compiler: Fehler!
...
String n= names.dequeue();
String m= names.dequeue();
• Das ist die Idee von Typ-Parametern.
• Kein cast und trotzdem nur der richtige Typ
• Aber: So ist es in Java nicht realisiert.
Komponenten – WS 2014/15 – Teil 5/Generics
8
Java: Liste von Objekten IV
class Queue<T> {
T[] daten;
...
public void enqueue(T elem) {...}
public T dequeue() {...}
...
}
• Für Klassen werden zusätzliche Parameter in spitzen Klammern
hinter dem Namen eingefügt, die innerhalb der Klasse dort stehen
dürfen, wo Typen stehen dürfen.
• Als Typen für T kommen nur Referenztypen in Frage, also Klassen
und Interfaces - verkürzt wird von nun an von Klassen gesprochen.
• Primitive Typen wie int oder float sind nicht als Werte erlaubt.
• Verabredungsgemäß werden Typ-Parameter mit einem Buchstaben
benannt – möglich wären aber auch Identifier mit mehreren
Buchstaben.
Komponenten – WS 2014/15 – Teil 5/Generics
9
Queue I - Ausgeprägter Typ
Queue<String> names= new Queue<String>();
names.enqueue("Haribald");
names.enqueue(42);
// Compiler: Fehler!
...
String n= names.dequeue();
String m= names.dequeue();
• Queue<String> kann als "Queue of String" ausgesprochen
werden.
• Leider(?) hat die Parameterversorgung von Klassen NICHT die
Semantik von Makros, da weitere Regeln existieren, die eine
Makroauffassung nicht erlauben.
Komponenten – WS 2014/15 – Teil 5/Generics
10
Begriffe
• Konkreter Typ = Ausgeprägter Typ = Generische Klasse mit
angegebenen Typen
• Generische Klasse = Klasse mit formalen Typparametern
• Bei der Klassendefinition sollte mit kurzen Namen in
Großbuchstaben gearbeitet werden - was nicht gerade schön ist,
wird aber so empfohlen.
• Es sind auch Listen von Parametern möglich:
class HashTable<K, V> { ....
oder
class HashTable<KeyType, ValueType> { ....
11
Komponenten – WS 2014/15 – Teil 5/Generics
Queue II - Rohtyp
Compiler
class Queue<T> {
class Queue {
T[] daten;
Object[] daten;
...
...
public void enqueue(T elem) { }
public void enqueue(Object elem) { }
public T dequeue() { }
public Object dequeue() { }
...
...
}
}
Programmiert
Laufzeit
Der Compiler übersetzt nun alle Varianten mit gesetztem Typparameter
zu einer einzigen (Code-)Instanz mit dem Typwert Object.
Zur Laufzeit sind daher die Typinformationen des Parameter nicht
vorhanden.
Komponenten – WS 2014/15 – Teil 5/Generics
12
Verwendungsregeln I
Die Regeln der Verwendung (Lesen, Zuweisen, cast) sind nun so
gewählt, dass der Compiler die richtige Verwendung prüfen kann.
Alles, was nur zur Laufzeit geprüft werden kann, ist verboten.
Queue
rq= new Queue<String>;
Queue<Integer> iq= new Queue<Integer>;
Queue<String> sq= iq;
// verboten
Warum?
iq.enqueue(17);
String val= sq.dequeue();
// autoboxing
// Integer!
•
•
// erlaubt
Rohtypen sind zuweisungskompatibel - aber: Das Mischen von
Rohtypen mit generischen Klassen gibt nur Probleme.
Unterschiedlich ausgeprägte Typen sind inkompatibel.
13
Komponenten – WS 2014/15 – Teil 5/Generics
Verwendungsregeln II
(1)class Queue
(2)class Queue<Object>
{ ...
{ ...
• Dies sind unterschiedliche Klassen, die in beiden Richtungen nicht
kompatibel sind.
• Dies gilt, obwohl alle Typparameter bei der internen Generierung
des Rohtyps in Object gewandelt werden.
• Oder: Das Konzept der <T> einfach als reinen Makromechanismus
ohne weitere Regeln aufzufassen, ist falsch (obwohl dies in vielen
Fällen sinnvoll wäre).
Komponenten – WS 2014/15 – Teil 5/Generics
14
Keine Felder von Typparametern I
class Queue<T> {
T[] daten;
T[] daten= new T[10];
...
// erlaubt
// verboten!
• Felder von Typparametern sind nicht erlaubt.
• Der Grund liegt in der Implementierungstechnik:
– der Compiler konvertiert intern den generischen Typ in den
Rohtyp, d.h. alle Parameter werden nach Object konvertiert.
– Wenn aus diesem Array gelesen wird, ist der tatsächlich
verwendete Typ im Array unbekannt.
15
Komponenten – WS 2014/15 – Teil 5/Generics
Keine Felder von Typparametern II
class Queue<T> {
T[] daten;
T[] daten= new T[10];
Object[] oArray= daten;
oArray[0]= "blabla";
....
}
//
//
//
//
//
erlaubt
Annahme: kein Fehler
erlaubt
nur dann in Ordnung, wenn
T ein String ist.
• Da die Typtests zur Compile-Zeit ablaufen und für diesen Fall dies
zur Laufzeit erforderlich ist (Denken Sie daran, dass Arrays auch
Methodenresultate sein können), wird dies einfach verboten.
• Beachten Sie auch die interne Konvertierung in einen Rohtyp.
Komponenten – WS 2014/15 – Teil 5/Generics
16
Keine Felder von Typparametern III
class Queue<T> {
T[] daten;
T[] daten= new T[10];
Object[] oArray= daten;
oArray[0]= "blabla";
oArray[1]= Integer(34);
...
}
//
//
//
//
//
in Ordnung
Annahme: kein Fehler
in Ordnung
Hmmm...
Und jetzt?
• Ein wichtiger Grund für das Verbot von generischen Arrays liegt
darin, dass nicht mehr sichergestellt werden kann, dass immer
alle Elemente vom selben kompatiblen Typ sind.
• Und wie sieht es hier mit der Typsicherheit aus?
• Das Erzeugen mit new() sei es für ein Objekt oder für ein Array
mit einem Parameter als Typ ist daher nicht erlaubt.
Komponenten – WS 2014/15 – Teil 5/Generics
17
Lösung für Arrays I
class Queue<T> {
T[] daten;
// in Ordnung
T[] daten= (T[])new Object[10]; // Warnung durch Compiler
...
}
Queue<String>[] qArray= new Queue<String>[10];
Queue<String>[] qArray= (Queue<String>[])new Queue<String>[10];
• Die erste Lösung ist das cast - was den Offenbarungseid des
gesamten Ansatzes darstellt.
• Der Compiler meldet eine Warnung, da hier etwas nicht geprüft
werden kann, d.h. es kommt auf die Disziplin der
Programmierers wieder an.
• Bei der Ausprägung sind auch wieder Arrays möglich.
Komponenten – WS 2014/15 – Teil 5/Generics
18
Lösung für Arrays II
class Container {
Object[] daten= new Object[10];
...
public void set(int index, String val) {...}
public String get(int index) { return (String)daten[index]; }
}
• Es wird eine eigene (normale) Klasse mit expliziter Typprüfung und
Typcasts für das Array geschrieben. Dadurch wird Typsicherheit
gewahrt.
• Statt daten wird dann diese Container-Klasse verwendet, sofern es
sich bei dem formalen Typparameterwert um String handelt.
• Nachteil: Für jeden Typ (als Parameterwert muss eine solche Klasse
geschrieben werden). Das wäre der Macro-Ansatz, wie er teilweise
in C++ benutzt wird.
Komponenten – WS 2014/15 – Teil 5/Generics
19
Lösung für Arrays III
public class Container<T> {
Object[] daten= new Object[10];
public void set(int index, T val) {
daten[0]= val;
}
public T get(int index) {
return (T)daten[index];
}
}
• Warum nicht hierfür eine generische Klasse verwenden?
• In Wirklichkeit ist das auch keine Lösung, aber die wohl beste:
– Nachteil: Es wird wie früher mit cast gearbeitet.
– Vorteil: Diese casts sind auf engem Raum geklammert.
Komponenten – WS 2014/15 – Teil 5/Generics
20
Also zusammengefasst...
public class Queue<T> {
T datum= ( T ) new Object();
T[] feld= (T[]) new Object[10];
}
So geht das mit den Objekten bzw. Arrays.
21
Komponenten – WS 2014/15 – Teil 5/Generics
Vererbung I
class Ober {
void meth() {...}
}
class Unter1 extends Ober {
void meth() {...}
}
class Unter2 extends Ober {
void meth() {...}
}
• Klasse Unter1 ist ein Untertyp von Klasse Ober.
• Gilt das auch für Queue<Unter1> und Queue<Ober>? NEIN.
Komponenten – WS 2014/15 – Teil 5/Generics
22
Vererbung II
(1)
(2)
(3)
(4)
Queue<Unter1> un= new Queue<Unter1>();
Queue<Ober>
ob= un;
// Annahme: wäre erlaubt
Ober obj= new Unter2();
// ist erlaubt
ob.enqueue(obj);
// müsste auch ok sein
un.dequeue() liefert später ein Objekt vom Typ Unter2!
Die Ursache ist die Zuweisung (2) oder die Parameterübergabe in Zeile (4).
(1)
(2)
(3)
(4)
Queue<Unter1> un= new Queue<Unter1>();
Queue<Ober>
ob= un;
// Annahme: wäre erlaubt
Ober obj= new Ober();
// ist erlaubt
ob.enqueue(obj);
// müsste auch ok sein
un.dequeue() liefert später ein Objekt vom Typ Ober statt Unter1!
Komponenten – WS 2014/15 – Teil 5/Generics
23
Vererbung III
• Das Problem etwas abstrakter:
Wenn ein Objekt einer Referenzvariablen, deren Typ in der
Vererbungshierarchie höher (also Richtung Wurzel) liegt,
zugewiesen wird, entsteht ein Informationsverlust über den Typ
des Objektes.
Dieser Verlust wird zur Laufzeit mittels eines Deskriptors
kompensiert.
• Durch die Zuweisung in Zeile (2) entsteht eigentlich eine
Vereinigung zweier Typen: Ober und Unter1. Objekte beider
Typen könnten dann in der Queue bzw. in der ausgeprägten
Klasse sein. Daher wird diese Zuweisung verboten.
• Dasselbe Problem tritt auch bei dem Problem mit den Arrays auf:
hier wurde nach der Zuweisung an eine Object-Variable der
Informationsverlust erzeugt.
Komponenten – WS 2014/15 – Teil 5/Generics
24
Regeln und Hinweise
• Unter/Oberklassen-Verhältnisse übertragen sich nicht auf die
korrespondierenden ausgeprägten Klassen.
• Parametrisierung und Vererbung beißen sich etwas - liegt eher
an der Vererbung bzw. Polymorphie.
• Mischungen von Rohtypen und generischen Typen bergen viele
Typprobleme.
Komponenten – WS 2014/15 – Teil 5/Generics
25
Einschränkungen I
interface Comparable<T> {
int compareTo(T other);
}
class SortedQueue<T extends Comparable<T>> {
...
public void enqueue(Object elem) {...
... elem.compareTo(daten[i]) ...
}
...
}
• Wenn eine beschränkende Bedingung an die möglichen
Typparameter erforderlich ist, hier dass der Typ die Schnittstelle
Comparable realisiert, weil eine Vergleichsoperation benötigt wird,
wird diese Bedingung durch "extends ..." ausgedrückt.
Komponenten – WS 2014/15 – Teil 5/Generics
26
Einschränkungen II
(1)... Klassenname<T extends Interfacename[<T>]> { ...
(2)... Klassenname<T extends Klassenname [<T>]> { ...
• Gleiche Buchstaben in dem <>-Teil bedeuten auch, dass immer
gleiche Werte gemeint sind. Soll der Typ T vor dem extends
eingeschränkt werden, muss auch T in der Einschränkung
vorkommen.
• "extends" bedeutet bei Schnittstellen: Der übergebene Typ T
muss die Schnittstelle realisieren, d.h. die Methoden der
Schnittstelle bereit stellen.
• "extends" bedeutet bei Klassen: Der übergebene Typ muss von
der Klasse abgeleitet sein bzw. diese Klasse sein.
• Bei Einschränkungen wird zum Übergang zum Rohtyp nicht
Object, sondern der hier angegebene Klassentyp benutzt.
27
Komponenten – WS 2014/15 – Teil 5/Generics
Einschränkungen III
class Ober {
void meth() {...}
}
class Unter1 extends Ober {
void meth() {...}
}
class Unter2 extends Ober {
void meth() {...}
}
class MyClass<T extends Ober>{
void myMethod (T obj) {
...
obj.meth();
...
}
}
• In diesem Beispiel darf als T nur Unter1, Unter2 und Ober
benutzt werden.
Komponenten – WS 2014/15 – Teil 5/Generics
28
Einschränkungen IV
class MyClass<T extends Ober>{
void myMethod (T obj) {
...
obj.meth();
...
}
}
• class MyClass<T> ... führt zum selben Rohtyp wie
class MyClass<T extends Object>, d.h. der Compiler setzt es
nach Object um.
• Bei class MyClass<T extends Ober> setzt der Compiler es nach
dem Typ Ober um.
Der generierte Rohtyp ist immer die Obergrenze innerhalb der
Vererbungshierarchie. Bisher hatte wir immer Object gehabt.
Komponenten – WS 2014/15 – Teil 5/Generics
29
Hinweis
(1) class MyClass<T> ...
(2) class MyClass<T extends Ober> ...
Welche Methoden können in der Klasse MyClass aufgerufen werden?
• Da in (1) alle Klassen als Parameter benutzt werden können, nur
die Methoden der Klasse Object - und dass ist nicht besonders viel.
• Selbst wenn nur als aktuelle Parameter die Klassen eines
eingeschränkten Klassenbaums benutzt werden und daher mehr
Methoden zur Laufzeit zu Verfügung stehen, können diese nicht
verwendet werden.
• Bei (2) können alle Methoden des "Vererbungsweges" von der
Klasse Object herunter bis zur Klasse Ober benutzt werden, nicht
jedoch Methoden aus Klassen, die von Ober abgeleitet wurden.
Komponenten – WS 2014/15 – Teil 5/Generics
30
Einschränkungen IV
(3) ... Klassenname<T extends N1 & N2 & N3...> { ...
class MyClass<T extends Ober & Comparable<T>>{
...
}
• Wenn sich die Bedingung auf mehrere Klassen bezieht, so wird
einfach eine Liste mit dem Trenner & angegeben.
• Da es keine Mehrfachvererbung gibt, dürfen in der Liste nur
eine Klasse, aber mehrere Interfaces stehen.
• Es muss zuerst die Klasse und dann die Liste der Interfaces
angegeben werden, also N1 ist eine Klasse oder ein Interface,
während N2 bis NN nur Interfaces sein können.
Komponenten – WS 2014/15 – Teil 5/Generics
31
Noch einmal Vererbung I
class MyClass<T> extends UpperClass {...}
UpperClass obj1= new MyClass<Integer>();
UpperClass obj2= new MyClass<String>();
class MyClass<T> extends Queue<String> {...}
Queue<String> obj3= new MyClass<Integer>();
Queue<String> obj4= new MyClass<String>();
• Die obigen Beispiele sind alle zulässig.
• Generische Klassen können fast wie normale Klassen behandelt
werden.
Komponenten – WS 2014/15 – Teil 5/Generics
32
Noch einmal Vererbung II
class MyClass<T> extends UpperClass<T> {...}
UpperClass<Integer> obj1= new MyClass<Integer>();
UpperClass<Integer> obj2= new MyClass<String>();
// OK
// verboten
class MyClass extends Queue<String> {...}
Queue<String> obj3= new MyClass();
MyClass
obj4= new MyClass();
• Eine nicht-generische (also normale) Klasse darf nur dann von
einer generischen Klasse erben, wenn diese ausgeprägt ist.
• Eine generische Klasse kann nur dann von einer anderen
generischen Klasse erben, wenn die Parametertypen
übereinstimmen, ansonsten entstehen Probleme bei der
Typsicherheit.
Komponenten – WS 2014/15 – Teil 5/Generics
33
Noch einmal Vererbung III
class MyQueue extends Queue<String> {
...
public void enqueue(String elem) {
...
}
class MyQueue extends Queue<T> {
...
public void enqueue(T elem) {
...
}
• Sollen Methoden in einer Unterklasse überschrieben werden, so müssen
die Signaturen entsprechend der Parametrisierung übereinstimmen.
• Bei einer ausgeprägten Klasse darf dann nicht mit Typparametern
gearbeitet werden, bei einer generischen mit dem Typparameter.
Komponenten – WS 2014/15 – Teil 5/Generics
34
Laufzeitabfragen und Prüfungen
(1)
(2)
(3)
(4)
(5)
Object obj= new Queue<String>();
if (obj instanceof Queue<String>) ....
if (obj instanceof String) ...
if (obj instanceof Queue) ...
Queue<String> neu= (Queue<String>)obj;
//
//
//
//
Verboten
OK
OK
Warnung
• (2) ist deshalb verboten, weil zur Laufzeit keine Informationen
über den Typparameter vorhanden sind, m.a.W. generische
Klassen existieren nur zur Compile-Zeit.
• Die Abfrage nach den Rohtypen ist aber erlaubt und liefert die
gewünschten Resultate.
• Ein cast zurück in die Welt der ausgeprägten generischen Klassen
wird immer mit einer Compiler-Warnung bestraft, da dieser dies
nicht prüfen kann.
Komponenten – WS 2014/15 – Teil 5/Generics
35
Wildcards I
Queue<String> obj1= new Queue<String>();
Queue obj2= new Queue<String>();
Queue<?> obj3= new Queue<String>();
Queue<?> obj4= new Queue<Integer>();
Queue<?> obj5= new Queue<Queue>();
• Ein konkreter Typ ist nur zu sich selbst kompatibel und zu seinem
Rohtyp.
• Ein konkreter Typ ist daher nie zu einem anderen konkreten Typ
kompatibel.
• Damit sind keine Objekte möglich, die mit der Menge aller
unterschiedlich ausgeprägter Typen kompatibel sind, m.a.W. eine
Collection von verschiedenen Queues lässt sich nicht realisieren, es
sei denn...
Es wird der Wildchard-Operator ? benutzt.
Komponenten – WS 2014/15 – Teil 5/Generics
36
Wildcards II
(1) Queue<?>
obj1= new Queue<String>();
(2) Queue<Integer> obj2= obj1;
// verboten
(3) Queue<String> obj3= obj1;
// verboten!
• Queue<?> ist die Obermenge zu Queue<Integer>,
Queue<String>, Queue<Blabla> etc.
• Damit lässt sich nicht viel anfangen, da ein Weg zurück zu
ausgeprägten Klassen nicht möglich ist. Warum?
• Zeile (3) wäre in Ordnung, aber nach Akzeptieren der Zeile (2)
würden Integer-Objekte in der Warteschlange sein, die in
Wirklichkeit Strings sind.
• Daher wird einfach alles verboten.
Grund: Es würde Überprüfungen zur Laufzeit erfordern.
37
Komponenten – WS 2014/15 – Teil 5/Generics
Wildcards III
(1)
(2)
(3)
(4)
(5)
Queue<? extends Ober> obj;
obj= new Queue<Ober>();
obj= new Queue<Unter1>();
obj= new Queue<Unter2>();
obj= new Queue<Integer>();
//
//
//
//
OK
OK
OK
verboten
• Die offene Spezifikation durch ? kann durch eine Liste von Klassen
hinter extends beschränkt werden, so dass Untermengen von
Klassen als Parameter möglich sind.
• Beispiel:
Sie wollen Warteschlangen für wichtige Ereignisse von denen für
unwichtige trennen: Queue<Alert> und Queue<Important> von
Queue<Message>.
Dies lässt sich über eine Vererbungshierarchie mit Alert,
Important und Message realisieren.
Komponenten – WS 2014/15 – Teil 5/Generics
38
Generische Methoden I
Wie lässt sich eine allgemeine Methode zur Bestimmung des
maximalen Wertes zweier Werte implementieren?
public <T extends Comparable<T>> T max(T left, T right) {
if(left.compareTo(right)>0) {
return left;
} else {
return right;
}
}
• Das <>-Konstrukt muss vor dem Rückgabetyp stehen.
• Dieselben Buchstaben bedeuten auch derselbe Typ.
• Das extends... besagt, dass alles erlaubt ist, was das Interface ...
implementiert, m.a.W. was compareTo() realisiert.
Komponenten – WS 2014/15 – Teil 5/Generics
39
Generische Methoden II
(1)String m= max("Albert Einstein", "Fritz Walter");
(2)int
w= max(10,-10);
(3)Queue<Integer> q1= new Queue<Integer>();
(4)Queue<Integer> q2= new Queue<Integer>();
(5)Queue<Integer> qint = max(q1, q2);
• (1) liefert natürlich "Fritz Walter", was beweist, dass Fußball
größer als Physik ist. (Java hat immer recht)
• (2) liefert 10.
• Und (5) einen Compiler-Fehler, da unsere Queues nicht das
Interface Comparable realisieren.
Komponenten – WS 2014/15 – Teil 5/Generics
40
Generische Methoden III
public Comparable max(Comparable left, Comparable right) {
if(left.compareTo(right)>0) {
return left;
} else {
return right;
}
}
• Dies hier ist eine alternative Implementierung ohne generische
Methoden, die dasselbe wie oben leistet.
• Warum? Weil die übergebenen Parameter eine gemeinsame
Oberklasse haben bzw. hier implementieren.
• Wenn das nicht gegeben ist, dann ist der generische Weg nicht
nur der bessere, sondern auch der einzige.
Komponenten – WS 2014/15 – Teil 5/Generics
41
Generische Methoden IV – Ein Beispiel
public <T> void enqueueArray(T[] liste, Queue<T> q) {
for(int i= 0; i<liste.length;i++) {
q.enqueue(liste[i]);
}
}
• Wenn eine Liste von Werten in eine Queue gebracht werden soll,
so kann dies mit der obigen Methode erfolgen.
• Dabei kann auf den 2. Parameter verzichtet und die
Daten/Methoden der eigenen Instanz verwendet werden.
• Dies ist einfach nur ein Beispiel, bei dem die betroffenen
Klassen/Objekte außer Object keine gemeinsame Oberklasse
haben.
Komponenten – WS 2014/15 – Teil 5/Generics
42
Nach dieser Anstrengung etwas Entspannung...
Komponenten – WS 2014/15 – Teil 5/Generics
43
Herunterladen