polimorfismo objeto metodos manejo instanciar genericos genericas generica datos con comparar clases java generics reflection

objeto - ¿Puedo instanciar reflexivamente un tipo genérico en Java?



objeto t en java (4)

La información genérica se pierde en tiempo de ejecución. No hay equivalente en tiempo de ejecución de una clase <String> Creador. Puede crear un tipo entre Creator y StringCreator que corrige el tipo genérico:

public interface Creator<T> { T create(); } public interface StringCreator extends Creator<String> { } public class StringCreatorImpl implements StringCreator { public String create() { return new String(); } } public class FancyStringCreator implements StringCreator { public String create() { return new StringBuffer().toString(); } } public static void main(String[] args) throws Exception { Class<?> someClass = Class.forName(args[0]); Class<? extends StringCreator> creatorClass = someClass.asSubclass(StringCreator.class); Constructor<? extends StringCreator> creatorCtor = creatorClass.getConstructor((Class<?>[]) null); Creator<String> creator = creatorCtor.newInstance((Object[]) null); }

Pero, por supuesto, pierde un poco de flexibilidad, porque no puede usar la siguiente clase de creador:

public class AnotherCreator implements Creator<String> { public String create() { return ""; } }

¿Es posible crear una instancia reflexiva de un tipo genérico en Java? Usando la técnica descrita aquí, me sale un error porque los tokens de clase no pueden ser genéricos. Toma el ejemplo a continuación. Quiero crear una instancia de alguna subclase del Creador que implemente el Creador. El nombre de clase real se pasa como un argumento de línea de comando. La idea es poder especificar una implementación de Creator en tiempo de ejecución. ¿Hay alguna otra manera de lograr lo que estoy tratando de hacer aquí?

public interface Creator<T> { T create(); } public class StringCreator implements Creator<String> { public String create() { return new String(); } } public class FancyStringCreator implements Creator<String> { public String create() { return new StringBuffer().toString(); } } public static void main(String[] args) throws Exception { Class<?> someClass = Class.forName(args[0]); /*ERROR*/Class<? extends Creator<String>> creatorClass = someClass.asSubclass(Creator.class); Constructor<? extends Creator<String>> creatorCtor = creatorClass.getConstructor((Class<?>[]) null); Creator<String> creator = creatorCtor.newInstance((Object[]) null); }

Editar: Me gusta el enfoque de Marcus como el más simple y pragmático sin eludir todo el asunto de los genéricos. Puedo usarlo en mi situación porque puedo especificar que la clase aprobada debe ser una subclase de StringCreator. Pero como Ericson señaló que la información genérica todavía está allí en el nivel de tipo, simplemente no en el nivel de tiempo de ejecución por lo que aún es posible examinar reflexivamente si una clase determinada implementa el tipo genérico correcto.


No estoy seguro de por qué está usando genéricos aquí.

La instanciación del objeto utilizando la reflexión sugeriría un uso general, pero presumiblemente llamarás a create en algún momento y asignarás el resultado a una String , de lo contrario, ¿por qué usar los genéricos para controlar el tipo de devolución?

Pero si escribiste la siguiente implementación de Creador:

public class IntegerCreator implements Creator<Integer> { public Integer create() { ... } }

Y lo pasó como un argumento que obtendría una ClassCastException al llamar create y asignar el resultado.


Esto hará lo que intenta hacer mientras proporciona seguridad de tipo. No hay forma de evitar una advertencia sin marcar, pero la verificación de tipo aquí justifica su supresión.

public static void main(String[] args) throws Exception { Class<? extends Creator<String>> clz = load(argv[0], String.class); Constructor<? extends Creator<String>> ctor = clz.getConstructor(); Creator<String> creator = ctor.newInstance(); System.out.println(creator.create()); } public static <T> Class<? extends Creator<T>> load(String fqcn, Class<T> type) throws ClassNotFoundException { Class<?> any = Class.forName(fqcn); for (Class<?> clz = any; clz != null; clz = clz.getSuperclass()) { for (Object ifc : clz.getGenericInterfaces()) { if (ifc instanceof ParameterizedType) { ParameterizedType pType = (ParameterizedType) ifc; if (Creator.class.equals(pType.getRawType())) { if (!pType.getActualTypeArguments()[0].equals(type)) throw new ClassCastException("Class implements " + pType); /* We''ve done the necessary checks to show that this is safe. */ @SuppressWarnings("unchecked") Class<? extends Creator<T>> creator = (Class<? extends Creator<T>>) any; return creator; } } } } throw new ClassCastException(fqcn + " does not implement Creator<String>"); }

La principal restricción que debe cumplir es que una clase en la jerarquía debe especificar el parámetro de tipo. Por ejemplo, la class MyCreator implements Creator<String> . No puede usarlo con la class GenericCreator<T> implements Creator<T> .

Actualmente no maneja el caso válido donde se crea una nueva interfaz de interface StringCreatorIfc extends Creator<String> y hace que una clase lo implemente. Podría mejorarse para hacer eso, pero lo dejo como un ejercicio para aquellos inclinados.


No necesitas esa línea. Tampoco necesitas el constructor ya que solo estás usando el predeterminado. Simplemente crea una instancia de la clase directamente:

public static void main(String[] args) throws Exception { Class<?> someClass = Class.forName(args[0]); Creator<String> creator = (Creator<String>) someClass.newInstance(); }

Si insistes, solo podrás llegar hasta la mitad:

public static void main(String[] args) throws Exception { Class<?> someClass = Class.forName(args[0]); Class<? extends Creator> creatorClass = someClass.asSubclass(Creator.class); Constructor<? extends Creator> creatorCtor = creatorClass.getConstructor((Class<?>[]) null); Creator<String> creator = (Creator<String>) creatorCtor.newInstance((Object[]) null); }