Creación de instancias de objeto de parámetro de tipo

88

Tengo una clase de plantilla de la siguiente manera:

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

¿Cómo creo una nueva instancia de T en mi clase?

Mercurioso
fuente

Respuestas:

85

Después del borrado de tipo, todo lo que se sabe Tes que es una subclase de Object. Necesita especificar alguna fábrica para crear instancias T.

Un enfoque podría usar un 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();
  }

}

El uso podría verse así:

MyClass<StringBuilder> it = new MyClass<>(StringBuilder::new);

Alternativamente, puede proporcionar un Class<T>objeto y luego usar la reflexión.

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

}
erickson
fuente
¿En qué paquete Supplierse encuentra? `MyClass (Class <? Extiende T> impl)` debe declarar `throws NoSuchMethodException` para ser compilado. Desafortunadamente, su respuesta no es amigable para los principiantes de Java.
purucat
@ user927387java.util.function.Supplier
erickson
El proveedor <T> requiere Java 8, JFTR para cualquier lugar que valga la pena.
Fran Marzoa
14

Otro enfoque no reflexivo es utilizar un patrón híbrido Builder / Abstract Factory.

En Effective Java, Joshua Bloch repasa el patrón Builder en detalle y aboga por una interfaz Builder genérica:

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

Los constructores de hormigón pueden implementar esta interfaz, y las clases externas pueden utilizar el constructor de hormigón para configurar el Constructor según sea necesario. El constructor se puede pasar a MyClass como Builder<T>.

Con este patrón, puede obtener nuevas instancias de T, incluso si Ttiene parámetros de constructor o requiere configuración adicional. Por supuesto, necesitará alguna forma de pasar el Constructor a MyClass. Si no puede pasar nada a MyClass, entonces Builder y Abstract Factory están fuera.


fuente
12

Esto puede ser más pesado de lo que está buscando, pero también funcionará. Tenga en cuenta que si adopta este enfoque, tendría más sentido inyectar la fábrica en MyClass cuando se construye en lugar de pasarla a su método cada vez que se llama.

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

class MyClass<T> 
{
    T field;
    public void myMethod(MyFactory<T> factory)
    {
       field = factory.newObject()
    }
}
Dan Hodge
fuente
1
Buen enfoque no reflexivo; la reflexión no siempre es una opción. myMethod debería poder aceptar un MyFactory <? extiende T>, ¿verdad?
erickson
1
Buena decisión: querrá poner un comodín limitado en la fábrica para permitir la creación de objetos de tipo T y subclases de T en myMethod ().
Dan Hodge
-3

Clase classOfT

        try {
            t = classOfT.newInstance();//new T(); NOTE: type parameter T cannot be instantiated directly
        } catch (Exception e) {
            e.printStackTrace();
        }
Kevendra
fuente
2
¿Dónde se declara esa clase classOfT?
Fran Marzoa