Java Generics

Category: 

Das Konzept der Generischen Programmierung wird in Java durch sogenannte Generics ermöglicht. Die Idee dieser parametrisierten Typen ist es, zusätzliche Variablen für Typen einzuführen. Zum Zeitpunkt der Implementierung ist dieser Typ unbekannt, erst bei der Verwendung wird ein konkreter Typ eingesetzt.

Warum Generics?

Um den Nutzen von Generics zu demonstrieren, ist es wohl am besten sich ein Beispiel mit und ohne Generics anzusehen. Für dieses Beispiel nehmen wir einmal an, dass wir eine Liste von Punkten speichern wollen.

ArrayList points = new ArrayList();
list.add(new Point(1,2));
list.add("Hello World"); // da ArrayList alle Objekte entgegen nimmt, ist dies kein Problem

Point p1 = (Point)points.get(0);
Point p2 = (Point)points.get(1); // Runtime Error

Erst einmal fällt auf, dass wir immer einen expliziten Typecast machen müssen, da die Liste nur Objects zurückgibt. Außerdem erhalten wir einen Runtime Error, da wir versuchen einen String in einen Point zu casten.

ArrayList<Point> points = new ArrayList<Point>();
list.add(new Point(1,2));
list.add("Hello World"); // nun sagt uns bereits der Compiler das was nicht stimmt

Point p1 = points.get(0);
Point p2 = points.get(1);

Durch die Generics fallen nun nicht nur die Typecasts weg, sondern wir erkennen einige Fehler sogar zur Compiletime.

Generics selbst anwenden

Klassen und Interfaces lassen sich relativ einfach um einen Typparameter erweitern, dazu wird dieser einfach in spitzen Klammern hinter den Klassennamen geschrieben. Innerhalb der Klasse kann man nun diese Typvariable wie einen echten Typen verwenden. Also als Parameter, Rückgabetyp und so weiter.

public class Test<T> {
  private T var;
  public T get() { return var; }
  public void set(T t) { this.var = t; }
}

Einschränkungen der Typvariable

Oft ist ein beliebiger Typ zu allgemein und man möchte die Typvariable weiter einschränken. Beispielsweise sollte eine sortierbare Liste nur Typen entgegen nehmen können, welche auch Comparable implementieren, damit wir auch sortieren können. Um zu sagen, dass ein Typ von einer bestimmten Klasse erben oder Interfaces implementieren soll, wird das Keyword extends verwendet.

class SortedList<T extends Comparable<T>> {
  //...
}

Wenn man möchte kann man auch weitere Einschränkungen machen, indem man diese mit einem &-verknüpft. In unserem Beispiel, soll die Klasse auch iterierbar sein.

class SortedList<T extends Comparable<T> & Iterable<T>> {
  //...
}

Bisher schränken wir die Typvariable immer nur auf eine bestimmte Klasse und deren Unterklassen ein. Umgekehrt kann man aber auch sagen, dass die Typvariable selbst eine Superklasse von einer bestimmten Klasse sein muss. Dies geht jedoch nur mit dem Wildcard Parameter '?'.

ArrayList<? super Integer> list = new ArrayList<Number>();