Instanziierendes Objekt vom Typ Parameter

87

Ich habe eine Vorlagenklasse wie folgt:

class MyClass<T>
{
    T field;
    public void myMethod()
    {
       field = new T(); // gives compiler error
    }
}

Wie erstelle ich eine neue Instanz von T in meiner Klasse?

Mercurious
quelle

Antworten:

83

Nach dem Löschen des Typs ist nur bekannt, Tdass es sich um eine Unterklasse von handelt Object. Sie müssen eine Factory angeben, um Instanzen von zu erstellen T.

Ein Ansatz könnte Folgendes verwenden Supplier<T>:

class MyClass<T> {

  private final Supplier<? extends T> ctor;

  private T field;

  MyClass(Supplier<? extends T> ctor) {
    this.ctor = Objects.requireNonNull(ctor);
  }

  public void myMethod() {
    field = ctor.get();
  }

}

Die Verwendung könnte folgendermaßen aussehen:

MyClass<StringBuilder> it = new MyClass<>(StringBuilder::new);

Alternativ können Sie ein Class<T>Objekt bereitstellen und dann die Reflexion verwenden.

class MyClass<T> {

  private final Constructor<? extends T> ctor;

  private T field;

  MyClass(Class<? extends T> impl) throws NoSuchMethodException {
    this.ctor = impl.getConstructor();
  }

  public void myMethod() throws Exception {
    field = ctor.newInstance();
  }

}
erickson
quelle
In welchem ​​Paket Supplierbefindet sich das? `MyClass (Class <? Erweitert T> impl)` muss deklarieren `löst NoSuchMethodException` aus, um kompiliert zu werden. Ihre Antwort ist leider nicht freundlich zu Java-Anfängern.
Purucat
@ user927387java.util.function.Supplier
erickson
Lieferant <T> benötigt Java 8, JFTR, wo immer es sich lohnt.
Fran Marzoa
14

Ein anderer nicht reflektierender Ansatz ist die Verwendung eines hybriden Builder / Abstract Factory-Musters.

In Effective Java geht Joshua Bloch das Builder-Muster im Detail durch und befürwortet eine generische Builder-Oberfläche:

public interface Builder<T> {
  public T build();
}

Concrete Builder können diese Schnittstelle implementieren, und externe Klassen können den Concrete Builder verwenden, um den Builder nach Bedarf zu konfigurieren. Der Builder kann als an MyClass übergeben werden Builder<T>.

Mit diesem Muster können Sie neue Instanzen von abrufen T, auch wenn TKonstruktorparameter vorhanden sind oder eine zusätzliche Konfiguration erforderlich ist. Natürlich benötigen Sie eine Möglichkeit, den Builder an MyClass zu übergeben. Wenn Sie nichts an MyClass übergeben können, sind Builder und Abstract Factory nicht verfügbar.


quelle
12

Dies ist möglicherweise schwerer als das, was Sie suchen, aber es wird auch funktionieren. Beachten Sie, dass es bei diesem Ansatz sinnvoller ist, die Factory bei der Erstellung in MyClass einzufügen, als sie bei jedem Aufruf an Ihre Methode zu übergeben.

interface MyFactory<T> 
{
    T newObject();
}

class MyClass<T> 
{
    T field;
    public void myMethod(MyFactory<T> factory)
    {
       field = factory.newObject()
    }
}
Dan Hodge
quelle
1
Guter, nicht reflektierender Ansatz; Reflexion ist nicht immer eine Option. myMethod sollte eine MyFactory <akzeptieren können? erweitert T>, richtig?
Erickson
1
Guter Aufruf - Sie sollten einen begrenzten Platzhalter in die Factory einfügen, damit Objekte vom Typ T und Unterklassen von T in myMethod () erstellt werden können.
Dan Hodge
-3

Klasse classOfT

        try {
            t = classOfT.newInstance();//new T(); NOTE: type parameter T cannot be instantiated directly
        } catch (Exception e) {
            e.printStackTrace();
        }
Kevendra
quelle
2
Wo wird diese classOfT-Klasse deklariert?
Fran Marzoa