Typargumente können entweder Referenztypen wie z.B. Integer
oder sogenannte Wildcards sein, wie die folgenden
Ableitungsregeln verdeutlichen:
ActualTypeArgument:
ReferenceType
Wildcard
Wildcard:
? WildcardBounds_opt
WildcardBounds:
extends ReferenceType
super ReferenceType
Der Wildcard steht für alle möglichen Typen als Platzhalter. Beispiele von
parametrisierten Typen, die als Typargument einen Wildcard enthalten, wären:
Vector<?>
Vector<? extends Number>
Vector<? super Integer>
Der Wildcard wird dabei durch ein Fragezeichen "?" repräsentiert und steht
für eine Reihe von möglichen Typen. Die drei unterschiedlichen Möglichkeiten zur
Verwendung von Wildcards sind:
-
?
: Ungebundener
Wildcard. Der Platzhalter steht für alle möglichen Typen.
-
? extends Type
:
Ein Wildcard mit einer oberen Schranke. Der Platzhalter steht für alle Typen,
die Type
erweitern und für Type
selbst.
-
? super Type
:
Ein Wildcard mit einer unteren Schranke. Der Platzhalter steht für alle Typen,
die Supertypen von Type
sind und für Type
selbst.
Der Wildcard kann nur eine Schranke haben (entweder eine obere oder eine
untere Schranke). Ein Typparamter kann im Gegensatz dazu mehrere Schranken besitzen.
Eine untere Schranke für Typparamter gibt es jedoch nicht. Das folgende Listing
besitzt drei unterschiedliche Methoden für die Konsolenausgabe der Elemente eines
Vektors. Jede dieser Methoden hat einen Parameter v
; gültige Typen
für diesen Paramter sind jeweils unterschiedlich eingeschränkt. Die erste Methode
lässt durch die Angabe Vector<?>
zu, dass die Elemente des
Vektors, der an sie übergeben wird, beliebig sein können.
public void printVector(Vector<?> v) { ... }
Die zweite Methode erzwingt durch den mit einem Wildcard parametrisierten
Typen Vector<? extends Number>
, dass die Elemente des Vektors
die Klasse Number
(obere Grenze) erweitern.
public void printVectorUB(Vector<? extends Number> v) { ... }
Würde diese Methode z.B. mit einem Argument vom parametrisierten Typ
Vector<Object>
aufgerufen, ist eine Fehlermeldung zu erwarten,
da die Element des Vektors vom allgemeinen Typ Object
wären (die
Klasse Object
erweitert Number
nicht).
Die dritte Methode setzt eine untere Schranke für die Elemente des Vektors, der
an diese Methode übergeben werden kann.
public void printVectorLB(Vector<? super Integer> v) { ... }
Die Elemente müssten im Beispiel entweder vom Typ Integer
sein
oder sie müssten eine Superklasse von Integer
sein (z.B. Number
oder die Klasse Object
). Der Aufruf der dritten Methode mit einem
Argument vom parametrisierten Typ Vector<Byte>
erzeugt eine
Fehlermeldung, da die Klassen Byte
und Integer
auf
derselben Ebene in der Vererbungshierarchie stehen (Byte
ist keine
Superklasse von Integer
).
Listing 3.14. GenericWildcardExample.java
. Verwendung den generischen Typs
Vector<E>
, der jeweils mit einem ungegbundenen Wildcard, einem
Wildcard mit oberer Schranke und einem Wildcard mit einer unteren Schranke
parametrisiert ist.
import java.util.*;
public class GenericWildcardExample {
public void printVector(Vector<?> v) {
for (Object o : v) {
System.out.print(o.toString() + " ");
}
System.out.println();
}
public void printVectorUB(Vector<? extends Number> v) {
for (Object o : v) {
System.out.print(o.toString() + " ");
}
System.out.println();
}
public void printVectorLB(Vector<? super Integer> v) {
for (Object o : v) {
System.out.print(o.toString() + " ");
}
System.out.println();
}
public static void main(String[] args) {
GenericWildcardExample gwe = new GenericWildcardExample();
Vector<Integer> vi = new Vector<Integer>();
vi.add(new Integer("3"));
vi.add(new Integer("4"));
gwe.printVector(vi);
gwe.printVectorUB(vi);
gwe.printVectorLB(vi);
Vector<Byte> vb = new Vector<Byte>();
vb.add(new Byte("1"));
vb.add(new Byte("2"));
gwe.printVector(vb);
gwe.printVectorUB(vb);
Vector<Object> vo = new Vector<Object>();
vo.add("a");
vo.add("b");
vo.add(new Integer("5"));
gwe.printVector(vo);
gwe.printVectorLB(vo);
}
}
Das Beispiel führt zur folgenden Konsolenausgabe:
3 4
3 4
3 4
1 2
1 2
a b 5
a b 5