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

88

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

class MyClass<T>
{
    T field;
    public void myMethod()
    {
       field = new T(); // gives compiler error
    }
}

Как мне создать новый экземпляр T в моем классе?

Меркурий
источник

Ответы:

85

После стирания типа все, что известно, 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.
Purucat
@ user927387java.util.function.Supplier
erickson
Поставщик <T> требует Java 8, JFTR везде, где стоит.
Fran Marzoa
14

Другой неотражающий подход - использование гибридного паттерна Builder / Abstract Factory.

В «Эффективной Java» Джошуа Блох подробно рассматривает шаблон Builder и отстаивает общий интерфейс Builder:

public interface Builder<T> {
  public T build();
}

Конкретные строители могут реализовать этот интерфейс, а внешние классы могут использовать конкретный конструктор для настройки Построителя по мере необходимости. Строитель может быть передан MyClass как Builder<T>.

Используя этот шаблон, вы можете получить новые экземпляры T, даже если они Tимеют параметры конструктора или требуют дополнительной настройки. Конечно, вам понадобится способ передать Builder в MyClass. Если вы не можете передать что-либо в MyClass, значит, Builder и Abstract Factory исключены.


источник
12

Это может быть тяжелее, чем то, что вы ищете, но это тоже сработает. Обратите внимание: если вы воспользуетесь этим подходом, было бы разумнее внедрить фабрику в MyClass, когда она создана, вместо того, чтобы передавать ее в свой метод при каждом ее вызове.

interface MyFactory<T> 
{
    T newObject();
}

class MyClass<T> 
{
    T field;
    public void myMethod(MyFactory<T> factory)
    {
       field = factory.newObject()
    }
}
Дэн Ходж
источник
1
Хороший, неотразимый подход; отражение не всегда вариант. myMethod должен иметь возможность принимать MyFactory <? расширяет T>, верно?
erickson
1
Хороший прием - вы захотите поместить в фабрику ограниченный подстановочный знак, чтобы разрешить создание объектов типа T и подклассов T в myMethod ().
Дэн Ходж
-3

Класс classOfT

        try {
            t = classOfT.newInstance();//new T(); NOTE: type parameter T cannot be instantiated directly
        } catch (Exception e) {
            e.printStackTrace();
        }
Кевендра
источник
2
Где объявлен этот класс classOfT?
Fran Marzoa