У меня есть класс шаблона следующим образом:
class MyClass<T>
{
T field;
public void myMethod()
{
field = new T(); // gives compiler error
}
}
Как мне создать новый экземпляр T в моем классе?
У меня есть класс шаблона следующим образом:
class MyClass<T>
{
T field;
public void myMethod()
{
field = new T(); // gives compiler error
}
}
Как мне создать новый экземпляр T в моем классе?
После стирания типа все, что известно о T
, это то, что это некоторый подкласс Object
. Вам нужно указать некоторую фабрику для создания экземпляров 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();
}
}
Supplier
? ` MyClass(Class‹? extends T› impl)` должен объявить ` throws NoSuchMethodException` для компиляции. К сожалению, ваш ответ не подходит для новичков в Java.
- person purucat; 12.09.2016
java.util.function.Supplier
- person erickson; 12.09.2016
Другой нерефлексивный подход заключается в использовании гибридного шаблона Builder/Abstract Factory.
В «Эффективной Java» Джошуа Блох подробно рассматривает шаблон Builder и защищает общий интерфейс Builder:
public interface Builder<T> {
public T build();
}
Конкретные построители могут реализовать этот интерфейс, а внешние классы могут использовать конкретный построитель для необходимой настройки построителя. Построитель может быть передан в MyClass как Builder<T>
.
Используя этот шаблон, вы можете получить новые экземпляры T
, даже если T
имеет параметры конструктора или требует дополнительной настройки. Конечно, вам понадобится какой-то способ передать Builder в MyClass. Если вы не можете ничего передать в MyClass, то Builder и Abstract Factory не работают.
Это может быть более тяжелым, чем то, что вы ищете, но это также сработает. Обратите внимание, что если вы выберете этот подход, будет разумнее внедрить фабрику в MyClass при ее создании, а не передавать ее в ваш метод каждый раз, когда он вызывается.
interface MyFactory<T>
{
T newObject();
}
class MyClass<T>
{
T field;
public void myMethod(MyFactory<T> factory)
{
field = factory.newObject()
}
}
Если вы хотите создать подкласс, вы также можете избежать стирания, проверьте http://www.artima.com/weblogs/viewpost.jsp?thread=208860
Одним из вариантов было бы бросить его с помощью Object
{field = (T) new Object();}
Первоначально поле будет иметь тип Object, но затем оно будет приведено к типу T. Это некрасиво, потому что уменьшение приведения к нулю — это то, что должно быть целью для инициализации объекта. Но я думаю, что это сработает.
Класс classOfT
try {
t = classOfT.newInstance();//new T(); NOTE: type parameter T cannot be instantiated directly
} catch (Exception e) {
e.printStackTrace();
}