Generics
Java
Auf dieser Seite
Generics sind eine coole Sache, im Detail bereiten sie aber Kopfschmerzen.
Instanziierung
Zu einem konkreten Beispiel der hier vorgestellten Lösung, das das Problem eventuell besser darstellt:
Problem
Ab und an habe ich das Problem, bei Klassen mit Generics eine Instanz des Generics erstellen zu wollen, also in etwa:
public class GenericClass<T extends Object> { public T getInstance() { return new T(); } } public class Test { public void testGenerics() { GenericClass<Integer> testObject = new GenericClass<>(); Integer a = testObject.getInstance(); } }
Das führt zu einem Compile-Fehler.
Lösung mit Klasse
Die Lösung, die ich im Netz gefunden habe, präferiert eine Zusatzklassenübergabe im Konstruktor (oder wo auch immer), von der Instanzen gebildet werden, also:
public class GenericClass<T extends Object> { private instanceClass<T> = null; public void init(final Class<T> theClass) { instanceClass = theClass; } public T getInstance() { try { return instanceClass.newInstance(); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } return null; } } public class Test { public void testGenerics() { GenericClass<Integer> testObject = new GenericClass<>(); testObject.init(Integer.class); Integer a = testObject.getInstance(); } }
Das finde ich unschön aus mehreren Gründen:
- Rückgriff auf Klassen-Objekte
- komplizierter Instance-Aufruf mit Exception-Handling
- häßlicher Code
Immerhin findet zur Compilezeit der Test statt, ob die Klassen und der generische Typ zusammenpassen.
Lösung mit Übergabe einer Factory-Methode
Derzeit präferiere ich die Lösung mit Factory-Methode aus mehreren Gründen:
- ich habe sowieso eine Factory für die verwendete Klasse
- keine Exception-Handling
- fühlt sich eleganter an
- Typprüfung zur Compilezeit
Es wird die create-Methode der Factory an die Generics-Klasse übergeben, die diese dann aufruft, funktioniert ab Java 8:
public class GenericClass<T extends Object> { private Supplier<T> instanceCall = null; public void init(final Supplier<T> theInstanceCall) { instanceCall = theInstanceCall; } public T getInstance() { return instanceCall.get(); } } public class ObjectFactory { public Integer createInteger() { return new Integer(); } } public class Test { public void testGenerics() { GenericClass<Integer> testObject = new GenericClass<>(); ObjectFactory factory = new ObjectFactory(); testObject.init(factory::createInteger); Integer a = testObject.getInstance(); } }