У меня есть класс шаблона следующим образом:
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
. Вам нужно указать некоторые 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();
}
}
Другой неотражающий подход заключается в использовании гибридного шаблона 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 отсутствуют.
Это может быть больше тяжеловеса, чем то, что вы ищете, но оно также будет работать. Обратите внимание, что если вы примете этот подход, было бы разумнее вводить 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
Класс classOfT
try {
t = classOfT.newInstance();//new T(); NOTE: type parameter T cannot be instantiated directly
} catch (Exception e) {
e.printStackTrace();
}
Лучшее и простое решение здесь использовать кастинг. Да, это выглядит ужасно, но лучше я видел здесь.
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();
}
}