Generics


Java
Ekkart Kleinod  • 

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();
    }

}