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.


Copyright © 2025 Thomas Smits