Creación de instancias de una clase genérica en Java [duplicado]
Sé que los genéricos de Java son algo inferiores a los de .Net.
Tengo una clase genérica Foo<T>
y realmente necesito crear una T
instancia Foo
usando un constructor sin parámetros. ¿Cómo se puede solucionar la limitación de Java?
Una opción es pasar Bar.class
(o cualquier tipo que le interese, cualquier forma de especificar la Class<T>
referencia adecuada) y mantener ese valor como campo:
public class Test {
public static void main(String[] args) throws IllegalAccessException,
InstantiationException {
Generic<Bar> x = new Generic<>(Bar.class);
Bar y = x.buildOne();
}
}
public class Generic<T> {
private Class<T> clazz;
public Generic(Class<T> clazz) {
this.clazz = clazz;
}
public T buildOne() throws InstantiationException, IllegalAccessException {
return clazz.newInstance();
}
}
public class Bar {
public Bar() {
System.out.println("Constructing");
}
}
Otra opción es tener una interfaz "de fábrica" y pasar una fábrica al constructor de la clase genérica. Eso es más flexible y no necesita preocuparse por las excepciones de reflexión.
Y esta es la implementación de Factory, como sugirió Jon Skeet :
interface Factory<T> {
T factory();
}
class Araba {
//static inner class for Factory<T> implementation
public static class ArabaFactory implements Factory<Araba> {
public Araba factory() {
return new Araba();
}
}
public String toString() { return "Abubeee"; }
}
class Generic<T> {
private T var;
Generic(Factory<T> fact) {
System.out.println("Constructor with Factory<T> parameter");
var = fact.factory();
}
Generic(T var) {
System.out.println("Constructor with T parameter");
this.var = var;
}
T get() { return var; }
}
public class Main {
public static void main(String[] string) {
Generic<Araba> gen = new Generic<Araba>(new Araba.ArabaFactory());
System.out.print(gen.get());
}
}
Producción:
Constructor with Factory<T> parameter
Abubeee
Aquí hay una forma bastante artificial de hacerlo sin usar explícitamente un argumento de constructor. Necesita extender una clase abstracta parametrizada.
public class Test {
public static void main(String [] args) throws Exception {
Generic g = new Generic();
g.initParameter();
}
}
import java.lang.reflect.ParameterizedType;
public abstract class GenericAbstract<T extends Foo> {
protected T parameter;
@SuppressWarnings("unchecked")
void initParameter() throws Exception, ClassNotFoundException,
InstantiationException {
// Get the class name of this instance's type.
ParameterizedType pt
= (ParameterizedType) getClass().getGenericSuperclass();
// You may need this split or not, use logging to check
String parameterClassName
= pt.getActualTypeArguments()[0].toString().split("\\s")[1];
// Instantiate the Parameter and initialize it.
parameter = (T) Class.forName(parameterClassName).newInstance();
}
}
public class Generic extends GenericAbstract<Foo> {
}
public class Foo {
public Foo() {
System.out.println("Foo constructor...");
}
}
Realmente necesito crear una instancia de una T en Foo usando un constructor sin parámetros
La respuesta simple es "no puedes hacer eso". Java usa el borrado de tipos para implementar genéricos que te impedirían hacer esto.
¿Cómo se puede solucionar la limitación de Java?
Una forma (podría haber otras) es pasar el objeto del que pasarías la instancia de T al constructor de Foo<T>
. O podría tener un método setBar(T theInstanceofT);
para obtener su T en lugar de crear una instancia en la clase misma.