Создание объекта типа параметра

У меня есть класс шаблона следующим образом:

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

Ответ 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();
    }
}