Tworzenie wystąpienia obiektu parametru typu

88

Mam następującą klasę szablonów:

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

Jak utworzyć nowe wystąpienie T w mojej klasie?

Rtęć
źródło

Odpowiedzi:

85

Po wymazaniu typów wiadomo Ttylko, że jest to podklasa klasy Object. Musisz określić fabrykę, aby utworzyć wystąpienia T.

W jednym podejściu można zastosować 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();
  }

}

Sposób użycia może wyglądać następująco:

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

Alternatywnie możesz podać Class<T>obiekt, a następnie użyć odbicia.

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
źródło
W jakim pakiecie się Supplierznajduje? `MyClass (Class <? Extends T> impl)` musi zadeklarować `throws NoSuchMethodException` do skompilowania. Twoja odpowiedź niestety nie jest przyjazna dla początkujących użytkowników języka Java.
purucat
@ user927387java.util.function.Supplier
erickson
Dostawca <T> wymaga Java 8, JFTR wszędzie tam, gdzie jest to warte.
Fran Marzoa
14

Innym nierefleksyjnym podejściem jest użycie hybrydowego wzorca Builder / Abstract Factory.

W Effective Java Joshua Bloch omawia szczegółowo wzorzec Builder i opowiada się za ogólnym interfejsem Builder:

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

Konstruktorzy betonu mogą implementować ten interfejs, a klasy zewnętrzne mogą używać konstruktora betonu do konfigurowania Konstruktora zgodnie z wymaganiami. Konstruktora można przekazać do MyClass jako plik Builder<T>.

Korzystając z tego wzorca, można uzyskać nowe wystąpienia programu T, nawet jeśli Tma parametry konstruktora lub wymaga dodatkowej konfiguracji. Oczywiście będziesz potrzebować jakiegoś sposobu, aby przekazać Konstruktora do MyClass. Jeśli nie możesz niczego przekazać do MyClass, to Builder i Abstract Factory są niedostępne.


źródło
12

To może być cięższe niż to, czego szukasz, ale też zadziała. Zauważ, że jeśli zastosujesz to podejście, bardziej sensowne byłoby wstrzyknięcie fabryki do MyClass podczas jej tworzenia, zamiast przekazywania jej do metody za każdym razem, gdy jest ona wywoływana.

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

class MyClass<T> 
{
    T field;
    public void myMethod(MyFactory<T> factory)
    {
       field = factory.newObject()
    }
}
Dan Hodge
źródło
1
Dobre, nierefleksyjne podejście; refleksja nie zawsze jest opcją. myMethod powinien być w stanie zaakceptować MyFactory <? rozszerza T>, prawda?
erickson
1
Dobre wywołanie - będziesz chciał umieścić ograniczony symbol wieloznaczny w fabryce, aby umożliwić tworzenie obiektów typu T i podklas T w myMethod ().
Dan Hodge,
-3

Klasa classOfT

        try {
            t = classOfT.newInstance();//new T(); NOTE: type parameter T cannot be instantiated directly
        } catch (Exception e) {
            e.printStackTrace();
        }
Kevendra
źródło
2
Gdzie jest zadeklarowana klasa classOfT?
Fran Marzoa