3.5. Gebundene Typparameter
Ein Typparameter kann mit einer Schranke (Bound) versehen werden. Für das
Nichtterminalsymbol TypeParamter
gelten
die folgenden Produktionen:
TypeParamter:
TypeVariable TypeBound_opt
TypeBound:
extends ClassOrInterfaceType AdditionalBoundList_opt
AdditionalBoundList:
AdditionalBound
AdditionalBoundList AdditionalBound
AddtionalBound:
& InterfaceType
Zu dem generischen Typ Holder<E>
können z.B. die
parametrisierten Typen
Holder<Integer>
Holder<String>
angegeben werden. Als Typargument des generischen Typs könnte ein beliebiger
Referenztyp angegeben werden. Wie die obigen Ableitungsregeln verdeutlichen kann
der Typparameter mit einer Schranke versehen werden. Eine Schranke beschränkt die
Referenztypen, die als Typargument verwendet werden können. Ein generischer Typ
mit einem gebundenen Typparamter könnte wie folgt angegeben werden:
NumberHolder<E extends Number>
Als aktuelle Typargumente könnten in diesem Fall nur Referenztypen, die
die Klasse Number
erweitern und Number
selbst verwendet
werden. Mögliche parametrisierte Typen zu diesem generischen Typ mit gebundenem
Typparameter wären:
NumberHolder<Byte>
NumberHolder<Integer>
NumberHolder<Number>
Der parametrisierte Typ NumberHolder<String>
wäre jedoch
nicht zulässig, da String
die Klasse Number
nicht
erweitert. Die Verwendung dieses parametrisierten Typs in einem Java-Quellcode
würde eine Fehlermeldung während der Compilierung erzeugen:
type parameter java.lang.String is not within its bound
Eine Definition des generischen Typs NumberHolder<E extends Number>
könnte wie folgt aussehen:
Listing 3.9. NumberHolder.java
. Verwendung eines Typparameters mit einer
Schranke.
public class NumberHolder<E extends Number> {
private E value;
public void set(E v) {
value = v;
}
public E get() {
return value;
}
}
Eine Typschranke eines Typparamters kann sich wiederum aus mehreren Schranken
zusammensetzen. Nach dem Schlüsselwort extends
folgt zunächst die
Angabe einer Klasse oder eines Interfaces (erste mögliche Schranke). Wie aus den
Albeitungsregeln (Nichtterminalsymbol TypeBound
)
hervorgeht können nach der Angabe des Schlüsselzeichens "&" noch weitere
Schranken für den Typparameter angegeben werden. Diese zusätzlichen Schranken
bestehen jedoch aus Interfaces (Klassenangaben sind hier nicht mehr erlaubt). Ein
generischer Typ, der als erste Schranke Number
und als zusätzliche
Schranke das Interface Comparable<E>
besitzt, könnte wie folgt
angegeben werden:
NumberCompareHolder<E extends Number & Comparable<E>>
Dieser generische Typ könnte z.B. mit Byte
, Integer
oder Double
parametrisiert werden, da diese Klassen Number
erweitern und gleichzeitig das Interface Comparable<E>
implementieren. Ein parametrisierter Typ NumberCompareHolder<Number>
ist hier jedoch nicht zulässig, da Number
außerhalb der zusätzlichen
Schranke liegt (das Interface wird nicht implementiert).
Das folgende Listing definiert den generischen Typ
NumberCompareHolder<E extends Number & Comparable<E>>
.
Mögliche Typargumente des generischen Typs müssen zusätzlich das Interface
Comparable<E>
implementieren (ermöglicht ein Vergleichen von
Objekten; z.B. kann der Maximalwert zweier Zahlen ermittelt werden).
Listing 3.10. NumberCompareHolder.java
.
public class NumberCompareHolder<E extends Number & Comparable<E>> {
private E number1;
private E number2;
public void set(E n1, E n2) {
number1 = n1;
number2 = n2;
}
public E getNumber1() {
return number1;
}
public E getNumber2() {
return number2;
}
public E max() {
if (number1.compareTo(number2) > 0) {
return number1;
} else {
return number2;
}
}
}
Das anschließende kurze Testprogramm verwendet die beiden zuvor definierten
generischen Typen, die jeweils unterschiedlich gebundene Typparameter besitzen.
Listing 3.11. TestNumberHolder.java
.
public class TestNumberHolder {
public static void main(String[] args) {
NumberHolder<Byte> nhb = new NumberHolder<Byte>();
nhb.set(new Byte("1"));
Byte b = nhb.get();
System.out.println(b.toString());
NumberHolder<Integer> nhi = new NumberHolder<Integer>();
nhi.set(new Integer("2"));
Integer i = nhi.get();
System.out.println(i.toString());
NumberHolder<Number> nhn = new NumberHolder<Number>();
nhn.set(new Byte("3"));
Number n1 = nhn.get();
System.out.println(n1.toString());
nhn.set(new Integer("4"));
Number n2 = nhn.get();
System.out.println(n2.toString());
NumberCompareHolder<Double> nch = new NumberCompareHolder<Double>();
nch.set(new Double(Math.random()), new Double(Math.random()));
Double d1 = nch.getNumber1();
Double d2 = nch.getNumber2();
Double dm = nch.max();
System.out.println("max(" + d1.toString() + "," + d2.toString() + ") = " +
dm.toString());
}
}
Eine mögliche Konsolenausgabe des Testprogramms wäre:
1
2
3
4
max(0.11537773347574398,0.3667514629967764) = 0.3667514629967764