663 Stimmen

Instanz eines generischen Typs in Java erstellen?

Ist es möglich, eine Instanz eines generischen Typs in Java zu erstellen? Basierend auf dem, was ich gesehen habe, denke ich, dass die Antwort lautet no ( aufgrund von Schriftlöschung ), aber es würde mich interessieren, ob jemand etwas sieht, das ich übersehe:

class SomeContainer<E>
{
    E createContents()
    {
        return what???
    }
}

EDIT: Es hat sich herausgestellt, dass Supertyp-Marken könnte verwendet werden, um mein Problem zu lösen, aber es erfordert eine Menge von Reflexion-basierten Code, wie einige der Antworten unten angegeben haben.

Ich werde das Thema noch eine Weile offen lassen, um zu sehen, ob jemand mit etwas kommt, das sich von Ian Robertsons Artima Artikel .

3 Stimmen

Habe gerade die Leistung auf einem Android-Gerät getestet. 10000 Operationen und: 8-9 ms dauert new SomeClass(), 9-11 ms dauert Factory<SomeClass>.createInstance() und 64-71 ms dauert shortest reflection: SomeClass z = SomeClass.class.newInstance(). Und alle Tests waren in einem einzigen try-catch-Block. Reflection newInstance() wirft 4 verschiedene Ausnahmen, erinnern Sie sich? Also habe ich beschlossen, das Factory-Muster zu verwenden

0 Stimmen

4 Stimmen

Mit Java 8 können Sie nun eine Konstruktorreferenz oder ein Lambda übergeben, wodurch dieses Problem ziemlich einfach zu umgehen ist. Siehe meine Antwort unten für Einzelheiten.

7voto

Neepsnikeep Punkte 179

Java erlaubt leider nicht, was Sie tun möchten. Siehe die offizielle Abhilfe :

Sie können keine Instanz eines Typparameters erstellen. Der folgende Code verursacht beispielsweise einen Kompilierungsfehler:

public static <E> void append(List<E> list) {
    E elem = new E();  // compile-time error
    list.add(elem);
}

Als Abhilfe können Sie ein Objekt eines Typparameters durch Reflexion erstellen:

public static <E> void append(List<E> list, Class<E> cls) throws Exception {
    E elem = cls.newInstance();   // OK
    list.add(elem);
}

Sie können die Append-Methode wie folgt aufrufen:

List<String> ls = new ArrayList<>();
append(ls, String.class);

0 Stimmen

Könnten Sie mir bitte sagen, warum Sie die Bewertung herabsetzen, wenn Sie dies tun? Ich sehe nicht ein, warum die offizielle Umgehung eine schlechte Lösung sein soll. Danke!

3 Stimmen

Ich nehme an, dass Sie Stimmen verlieren, weil Ihre Antwort im Wesentlichen die gleiche ist wie die von Justin Rudd: stackoverflow.com/a/75254/103412

7voto

jb. Punkte 21920

Wenn Sie den Klassennamen bei der Instanziierung nicht zweimal eingeben wollen, wie in:

new SomeContainer<SomeType>(SomeType.class);

Sie können die Fabrikmethode verwenden:

<E> SomeContainer<E> createContainer(Class<E> class); 

Wie in:

public class Container<E> {

    public static <E> Container<E> create(Class<E> c) {
        return new Container<E>(c);
    }

    Class<E> c;

    public Container(Class<E> c) {
        super();
        this.c = c;
    }

    public E createInstance()
            throws InstantiationException,
            IllegalAccessException {
        return c.newInstance();
    }

}

7voto

Mike Stone Punkte 43560

Hier ist eine Option, die ich mir ausgedacht habe, vielleicht hilft sie:

public static class Container<E> {
    private Class<E> clazz;

    public Container(Class<E> clazz) {
        this.clazz = clazz;
    }

    public E createContents() throws Exception {
        return clazz.newInstance();
    }
}

EDIT: Alternativ können Sie auch diesen Konstruktor verwenden (der allerdings eine Instanz von E erfordert):

@SuppressWarnings("unchecked")
public Container(E instance) {
    this.clazz = (Class<E>) instance.getClass();
}

0 Stimmen

Ja, das funktioniert auch ohne Generika - mit Generika wird die Instanziierung dieses Containers ein wenig überflüssig (man muss zweimal angeben, was "E" ist).

0 Stimmen

Nun, das passiert, wenn man Java und Generics verwendet... sie sind nicht schön, und es gibt starke Einschränkungen...

6voto

Se Song Punkte 1483

Ich hoffe, es ist nicht zu spät, um zu helfen!!!

Java ist typsicher, d.h. nur Objekte können Instanzen erzeugen.

In meinem Fall kann ich keine Parameter an die createContents Methode. Meine Lösung ist mit erweitert im Gegensatz zu der Antwort unten.

private static class SomeContainer<E extends Object> {
    E e;
    E createContents() throws Exception{
        return (E) e.getClass().getDeclaredConstructor().newInstance();
    }
}

Dies ist mein Beispielfall, in dem ich keine Parameter übergeben kann.

public class SomeContainer<E extends Object> {
    E object;

    void resetObject throws Exception{
        object = (E) object.getClass().getDeclaredConstructor().newInstance();
    }
}

Mit Reflexion erstellen Laufzeitfehler, wenn Sie Ihre generische Klasse mit keinem Objekttyp erweitert. Erweitern Sie Ihren generischen Typ zu einem Objekt und konvertieren Sie diesen Fehler in einen Kompilierzeitfehler.

5voto

Sie können verwenden:

Class.forName(String).getConstructor(arguments types).newInstance(arguments)

Sie müssen jedoch den genauen Klassennamen angeben, einschließlich der Pakete, z. B. java.io.FileInputStream . Ich habe dies verwendet, um einen Parser für mathematische Ausdrücke zu erstellen.

15 Stimmen

Und wie erhalten Sie den genauen Klassennamen des generischen Typs zur Laufzeit?

0 Stimmen

Sie müssen es mit einer Instanz dieser Klasse speichern. Machbar, wenn auch kaum bequem. Wenn Ihre generische Klasse ein Mitglied des Typs E (oder T oder was auch immer) hat, ist die Abfrage des binären Namens einfach foo.getClass().getName() . Woher kommt DIESE Instanz? Ich übergebe gerade einen in einen Konstruktor in dem Projekt, an dem ich gerade arbeite.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X