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:
Po wymazaniu typów wiadomo T
tylko, ż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();
}
}
java.util.function.Supplier
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 T
ma 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.
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()
}
}
Jeśli chcesz dołączyć do podklasy, możesz również uniknąć wymazywania, sprawdź http://www.artima.com/weblogs/viewpost.jsp?thread=208860
Klasa classOfT
try {
t = classOfT.newInstance();//new T(); NOTE: type parameter T cannot be instantiated directly
} catch (Exception e) {
e.printStackTrace();
}
Supplier
znajduje? `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.