Weitere Themen
Beispiel: Multiple Typ-Parameter
public class Pair<T, V> {
private T o1;
private V o2;
public Pair(T o1, V o2) {
this.o1 = o1;
this.o2 = o2;
}
public T getFirst() {
return o1;
}
public V getSecond() {
return o2;
}
}
Die Anzahl der Typ-Parameter ist nicht beschränkt, man kann beliebig viele haben. Allerdings leidet die Übersichtlichkeit des Programms erheblich, wenn mehr als drei Typ-Parameter zum Einsatz kommen.
Neue Instanzen der Klasse Pair
würde man dann z. B. mit folgendem Code erzeugen:
Pair<String, String> p1 = new Pair<>("Hello", "World");
Pair<Integer, String> geld = new Pair<>(100, "EUR");
Generische Methoden
Auch Methoden können Typ-Parameter haben
- Sowohl statische als auch nicht-statische Methoden können als generische Methoden deklariert werden, z. B.
static <E> List<E> asList(E[] a)
- Im Gegensatz zu Klassen muss der Verwender den Typ-Parameter nicht explizit setzen, der Compiler leitet ihn aus den Typen des Aufrufs ab: Typ-Inferenz (type inference)
- In seltenen Fällen muss man den Typ für die Methode explizit angeben
Beispiel: Generische Methoden
public class Zusammenfasser {
public static <T> SimpleStack<T> fasseZusammen(SimpleStack<T> s1, SimpleStack<T> s2) {
SimpleStack<T> ergebnis = new SimpleStack<T>(s1.getSize()
+ s2.getSize());
for (int i = s1.getSize(); i >= 0; i--) {
ergebnis.push(s1.pop());
}
for (int i = s2.getSize(); i >= 0; i--) {
ergebnis.push(s2.pop());
}
return ergebnis;
}
}
Bei generischen Methoden gibt es neben den normalen Parametern, die in den Klammern stehen noch Typ-Parameter. Diese werden, wie bei Klassen auch, in spitzen Klammern (<T>
) notiert. Der Typ-Parameter wird, obwohl er ein Parameter ist, vor dem Rückgabetyp der Methode angegeben.
Im vorliegenden Beispiel muss man beachten, dass das erste <T>
die Deklaration des Typ-Parameters ist, das zweite in SimpleStack<T>
ist bereits eine Verwendung, genauso wie es sich bei SimpleStack<T> s1
und SimpleStack<T> s2
um Verwendungen handelt.
SimpleStack<String> stack1 = new SimpleStack<>(10);
stack1.push("Hello");
stack1.push("World");
SimpleStack<String> stack2 = new SimpleStack<>(10);
stack2.push("!");
SimpleStack<String> ergebnis = Zusammenfasser.fasseZusammen(stack1, stack2);
Beispiel: Generische Methoden (ohne Typ-Inferenz)
public class Unifier {
public <E> Set<E> unify(Set<? extends E> s1, Set<? extends E> s2) {
Set<E> s = new HashSet<E>();
s.addAll(s1);
s.addAll(s2);
return s;
}
}
Set<Integer> s1 = new HashSet<>();
s1.add(1); s1.add(3);
Set<Double> s2 = new HashSet<>();
s2.add(10.0); s2.add(30.0);
Set<Number> union = new Unifier().<Number>unify(s1, s2);
System.out.println(union);
In diesem Beispiel muss der Typ für den Typ-Parameter E
explizit angegeben werden. Der Hintergrund ist, dass der Compiler aus den Parametern der Methode den Typ nicht mehr ableiten kann, da die beiden Generics s1
und s2
unterschiedliche Typ-Parameter haben. Daher muss hier mit <Number>
explizit angegeben werden, welchen Wert E annehmen soll.