Istanziare un oggetto di tipo parametro


88

Ho una classe modello come segue:

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

Come creo una nuova istanza di T nella mia classe?

Risposte:


85

Dopo la cancellazione del tipo, tutto ciò che si sa Tè che si tratta di una sottoclasse di Object. È necessario specificare una fabbrica di cui creare istanze T.

Un approccio potrebbe utilizzare 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();
  }

}

L'utilizzo potrebbe essere simile a questo:

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

In alternativa, puoi fornire un Class<T>oggetto e quindi utilizzare la riflessione.

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

}

In quale pacchetto Suppliersi trova? `MyClass (Class <? Extends T> impl)` deve dichiarare `throws NoSuchMethodException` per essere compilato. La tua risposta purtroppo non è amichevole per i principianti di Java.
purucat

@ user927387java.util.function.Supplier
erickson

Il fornitore <T> richiede Java 8, JFTR ovunque valga la pena.
Fran Marzoa

14

Un altro approccio non riflettente consiste nell'usare un modello ibrido Builder / Abstract Factory.

In Effective Java, Joshua Bloch esamina in dettaglio il pattern Builder e sostiene un'interfaccia Builder generica:

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

I costruttori di calcestruzzo possono implementare questa interfaccia e le classi esterne possono utilizzare il generatore di calcestruzzo per configurare il generatore come richiesto. Il builder può essere passato a MyClass come file Builder<T>.

Utilizzando questo modello, è possibile ottenere nuove istanze di T, anche se Tdispone di parametri del costruttore o richiede una configurazione aggiuntiva. Ovviamente, avrai bisogno di un modo per passare il Builder in MyClass. Se non puoi passare nulla a MyClass, Builder e Abstract Factory sono fuori.


12

Questo potrebbe essere più pesante di quello che stai cercando, ma funzionerà anche. Nota che se adotti questo approccio, avrebbe più senso iniettare la factory in MyClass quando viene costruita invece di passarla nel tuo metodo ogni volta che viene chiamata.

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

class MyClass<T> 
{
    T field;
    public void myMethod(MyFactory<T> factory)
    {
       field = factory.newObject()
    }
}

1
Buon approccio non riflettente; la riflessione non è sempre un'opzione. myMethod dovrebbe essere in grado di accettare un MyFactory <? estende T>, giusto?
erickson

1
Buona chiamata: ti consigliamo di inserire un carattere jolly limitato nella factory per consentire la creazione di oggetti di tipo T e sottoclassi di T in myMethod ().
Dan Hodge,


-3

Class classOfT

        try {
            t = classOfT.newInstance();//new T(); NOTE: type parameter T cannot be instantiated directly
        } catch (Exception e) {
            e.printStackTrace();
        }

2
Dove viene dichiarata la classe classOfT?
Fran Marzoa
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.