Создание объекта типа параметра
У меня есть класс шаблона следующим образом:
class MyClass<T>
{
T field;
public void myMethod()
{
field = new T(); // gives compiler error
}
}
Как создать новый экземпляр T в моем классе?
Ответы
Ответ 1
После стирания типа все, что известно о T
, состоит в том, что это некоторый подкласс Object
. Вам нужно указать некоторые factory для создания экземпляров T
.
В одном подходе можно использовать 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();
}
}
Использование может выглядеть так:
MyClass<StringBuilder> it = new MyClass<>(StringBuilder::new);
В качестве альтернативы вы можете предоставить объект Class<T>
, а затем использовать отражение.
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();
}
}
Ответ 2
Другой неотражающий подход заключается в использовании гибридного шаблона Builder/Abstract Factory.
В Эффективной Java Джошуа Блох подробно описывает шаблон Builder и выступает за общий интерфейс Builder:
public interface Builder<T> {
public T build();
}
Конкретные сборщики могут реализовать этот интерфейс, а внешние классы могут использовать конкретный строитель для настройки Builder по мере необходимости. Строитель может быть передан MyClass как Builder<T>
.
Используя этот шаблон, вы можете получить новые экземпляры T
, даже если T
имеет параметры конструктора или требует дополнительной настройки. Конечно, вам нужно каким-то образом передать Builder в MyClass. Если вы не можете передать что-либо в MyClass, то Builder и Abstract Factory отсутствуют.
Ответ 3
Это может быть больше тяжеловеса, чем то, что вы ищете, но оно также будет работать. Обратите внимание, что если вы примете этот подход, было бы разумнее вводить factory в MyClass, когда он будет создан вместо того, чтобы передавать его в ваш метод каждый раз, когда он вызывается.
interface MyFactory<T>
{
T newObject();
}
class MyClass<T>
{
T field;
public void myMethod(MyFactory<T> factory)
{
field = factory.newObject()
}
}
Ответ 4
Если вы готовы к подклассу, вы также можете избежать стирания, проверьте
http://www.artima.com/weblogs/viewpost.jsp?thread=208860
Ответ 5
Класс classOfT
try {
t = classOfT.newInstance();//new T(); NOTE: type parameter T cannot be instantiated directly
} catch (Exception e) {
e.printStackTrace();
}
Ответ 6
Лучшее и простое решение здесь использовать кастинг. Да, это выглядит ужасно, но лучше я видел здесь.
class MyClass<T>
{
T field;
public void myMethod()
{
field = (T)new Object(); // works
}
}
Протестировано с помощью следующего кода:
public class MyClass<T>
{
T field;
public void myMethod()
{
field = (T)new Object(); // works
}
public static void main(String[] args) {
MyClass<Integer> myclass = new MyClass<>();
myclass.field=2;
myclass.myMethod();
}
}