Datentypen sind in Java ja relativ einfach: int, char, double, float, String, boolean deren Wrapper-Klassen sowie eigene Objekte und am Ende ist doch alles nur ein Object, oder? Doch irgendwie gibt es da noch mehr denn was ist denn eigentlich dieser komische Typ T den man schon mal irgendwo gesehen hat?
In diesem Beitrag soll es um generische Datentypen gehen und ich möchte dir an einem kurzen Beispiel zeigen, wie sie anzuwenden sind.
Generische Datentypen in Java, auch Generics genannt, ermöglichen die Definition von Klassen, Interfaces und Methoden mit Typvariablen. Das klingt erst mal ziemlich wild, wenn man sich damit noch nie beschäftigt hat.
Doch das erklärte Ziel dahinter ist es, dass man dadurch Code wiederverwenden kann, ohne an bestimmte Datentypen gebunden zu sein. Das verbessert die Typsicherheit und erleichtert die Wartung von Code, indem zur Compile-Zeit Fehler verhindert werden, die durch falsche Typzuweisungen entstehen könnten. Generics werden in vielen Bibliotheken verwendet, darunter beispielsweise in den Java Collection Frameworks (List<T>, Map<K, V>).
Doch schauen wir uns das mal in Form von Code an. Stellen wir uns vor, wir möchten eine Box erstellen, die einen Wert beliebigen Typs speichern soll. Ohne Generics müssten wir ja für jeden Datentyp, den unsere Box speichern kann, eine eigene Klasse schreiben. Das führt aber zu schlechter Wartbarkeit und widerspricht auch irgendwie dem CleanCode-Gedanken, ganz abgesehen davon, dass wir schon zu Beginn an festlegen müssten, welche Datentypen wir überhaupt unterstützen wollen, was bei einer späteren Erweiterung immer zu Problemen führt.
Erstellen wir also eine generische Box:
class Box<T> {
private T inhalt;
public void setInhalt(T inhalt) {
this.inhalt = inhalt;
}
public T getInhalt() {
return inhalt;
}
}
Sieht ziemlich easy aus, oder? Wie du sehen kannst, steht hier das T für einen beliebigen Typ den unsere Box speichern kann. Schauen wir uns nun an, wie wir das auch einsetzen können:
public class GenericsDemo {
public static void main(String[] args) {
// Eine Box fuer ganze Zahlen (Integer)
Box<Integer> intBox = new Box<>();
intBox.setInhalt(42);
System.out.println("Integer Box: " + intBox.getInhalt());
// Eine Box für Zeichenketten (String)
Box<String> stringBox = new Box<>();
stringBox.setInhalt("Hallo Generics!");
System.out.println("String Box: " + stringBox.getInhalt());
}
}
Hier können wir nun sehen, dass die generische Klasse Box den Platzhalter T enthält. Dieser wird zur Laufzeit (nicht zur Compile-Zeit!) durch einen konkreten Typ ersetzt. Daneben gibt es typische Getter und Setter, die ebenfalls durch den Platzhalter T, generische Typen in unserer Box lesen oder setzen können. Erst bei der Instanziierung einer Box legen wir konkret fest, welchen Typ unsere Box denn genau haben soll und verhindern im weiteren Verlauf, dass es ein anderer Typ sein kann.
Vielleicht kommt dir das bekannt vor denn zum Beispiel bei der Instanziierung einer ArrayList machen wir das genau so:
List<String> myStringList = new ArrayList<>();
Auch hier legen wir erst bei der Instanziierung fest, von welchem Typ unsere List sein soll, denn grundsätzlich kann die Liste von einen beliebigen Typen sein.
Doch obwohl Generics ein sehr mächtiges Werkzeug sind, gibt es auch Einschränkungen denn es ist zum Beispiel nicht möglich, primitive Datentypen als Typparameter zu verwenden. Hier müssen dafür die entsprechenden Wrapper-Klassen verwendet werden. Des Weiteren kann ein generischer Typ niemals als generisches Objekt instanziiert werden - hier muss immer der konkreten Typ verwendet werden. Außerdem kann mit dem generischen Typen kein instanceof genutzt werden (if (obj instanceof T) führt zu einem Compile-Fehler).
Generische Datentypen machen Java-Code aber flexibler, typsicherer und besser wartbar. Es ist wesentlich einfacher einen generischen Typen zu verwenden, als für jeden Typ eine eigene Klasse zu erstellen. Generics werden aber auch in vielen Bibliotheken verwendet, darunter die Java Collection Frameworks (List<T>, Map<K, V>).
Ich hoffe, dieses kleine Beispiel hat die einen guten Überblick verschafft, wozu man Generics gebrauchen kann und warum sie sinnvoll sind. Hast du auch bereits Erfahrungen damit sammeln können oder bist schon einmal darüber gestolpert und hast dich gefragt wozu das da ist? Lass es mich gern in den Kommentaren wissen.
