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?

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

}

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.


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

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

2
Gdzie jest zadeklarowana klasa classOfT?
Fran Marzoa
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.