objetos - Instanciando genéricos tipo en java
metodos genericos java (12)
Me gustaría crear un objeto de Generics Type en java. Por favor, sugiero cómo puedo lograr lo mismo.
Nota: Esto puede parecer un problema genérico trivial. Pero apuesto ... no lo es. :)
supongamos que tengo la declaración de clase como:
public class Abc<T> {
public T getInstanceOfT() {
// I want to create an instance of T and return the same.
}
}
Escriba solución de borrado
Inspirado por la respuesta de @martins , escribí una clase de ayuda que me permite resolver el problema del borrado de tipos. Usando esta clase (y un pequeño truco feo) puedo crear una nueva instancia de un tipo de plantilla:
public abstract class C_TestClass<T > {
T createTemplateInstance() {
return C_GenericsHelper.createTemplateInstance( this, 0 );
}
public static void main( String[] args ) {
ArrayList<String > list =
new C_TestClass<ArrayList<String > >(){}.createTemplateInstance();
}
}
El truco feo aquí es hacer que la clase sea abstract
modo que el usuario de la clase se ve obligado a subtitularla. Aquí estoy subclasificando al agregar {}
después de la llamada al constructor. Esto define una nueva clase anónima y crea una instancia de la misma.
Una vez que la clase genérica se subtipo con tipos concretos de plantilla, puedo recuperar los tipos de plantilla.
public class C_GenericsHelper {
/**
* @param object instance of a class that is a subclass of a generic class
* @param index index of the generic type that should be instantiated
* @return new instance of T (created by calling the default constructor)
* @throws RuntimeException if T has no accessible default constructor
*/
@SuppressWarnings( "unchecked" )
public static <T> T createTemplateInstance( Object object, int index ) {
ParameterizedType superClass =
(ParameterizedType )object.getClass().getGenericSuperclass();
Type type = superClass.getActualTypeArguments()[ index ];
Class<T > instanceType;
if( type instanceof ParameterizedType ) {
instanceType = (Class<T > )( (ParameterizedType )type ).getRawType();
}
else {
instanceType = (Class<T > )type;
}
try {
return instanceType.newInstance();
}
catch( Exception e ) {
throw new RuntimeException( e );
}
}
}
En el código que publicaste, es imposible crear una instancia de T
ya que no sabes qué tipo es:
public class Abc<T>
{
public T getInstanceOfT()
{
// There is no way to create an instance of T here
// since we don''t know its type
}
}
Por supuesto, es posible si tiene una referencia a Class<T>
y T
tiene un constructor predeterminado, simplemente llame a newInstance()
en el objeto Class
.
Si subclase Abc<T>
, puede incluso solucionar el problema del borrado de tipos y no tendrá que pasar ninguna referencia de Class<T>
torno a:
import java.lang.reflect.ParameterizedType;
public class Abc<T>
{
T getInstanceOfT()
{
ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass();
Class<T> type = (Class<T>) superClass.getActualTypeArguments()[0];
try
{
return type.newInstance();
}
catch (Exception e)
{
// Oops, no default constructor
throw new RuntimeException(e);
}
}
public static void main(String[] args)
{
String instance = new SubClass().getInstanceOfT();
System.out.println(instance.getClass());
}
}
class SubClass
extends Abc<String>
{
}
En primer lugar, no puede acceder al parámetro de tipo T
en el método principal estático, solo en miembros de clase no estáticos (en este caso).
En segundo lugar, no puede instanciar T
porque Java implementa genéricos con Type Erasure . Casi toda la información genérica se borra en el momento de la compilación.
Básicamente, no puedes hacer esto:
T member = new T();
Aquí hay un buen tutorial sobre angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html .
Estaba implementando lo mismo usando el siguiente enfoque.
public class Abc<T>
{
T myvar;
public T getInstance(Class<T> clazz) throws InstantiationException, IllegalAccessException
{
return clazz.newInstance();
}
}
Estaba tratando de encontrar una mejor manera de lograr lo mismo.
¿No es posible?
Hay formas estrafalarias alrededor de esto cuando realmente tienes que hacerlo.
Aquí hay un ejemplo de un método de transformación que me parece muy útil; y proporciona una forma de determinar la clase concreta de un genérico.
Este método acepta una colección de objetos como entrada, y devuelve una matriz donde cada elemento es el resultado de llamar a un campo getter en cada objeto en la colección de entrada. Por ejemplo, supongamos que tiene una List<People>
y quiere una String[]
contenga el apellido de todos.
El tipo de valor de campo devuelto por el getter es especificado por el E
genérico, y necesito crear una instancia de un array de tipo E[]
para almacenar el valor de retorno.
El método en sí es un poco feo, pero el código que escribe que lo usa puede ser mucho más limpio.
Tenga en cuenta que esta técnica solo funciona cuando en algún lugar de los argumentos de entrada hay un objeto cuyo tipo coincide con el tipo de retorno, y puede determinarlo de manera determinista. Si las clases concretas de sus parámetros de entrada (o sus subobjetos) no pueden decirle nada sobre los genéricos, entonces esta técnica no funcionará.
public <E> E[] array (Collection c) {
if (c == null) return null;
if (c.isEmpty()) return (E[]) EMPTY_OBJECT_ARRAY;
final List<E> collect = (List<E>) CollectionUtils.collect(c, this);
final Class<E> elementType = (Class<E>) ReflectionUtil.getterType(c.iterator().next(), field);
return collect.toArray((E[]) Array.newInstance(elementType, collect.size()));
}
El código completo está aquí: https://github.com/cobbzilla/cobbzilla-utils/blob/master/src/main/java/org/cobbzilla/util/collection/FieldTransformer.java#L28
La única forma de hacerlo funcionar es usar Geneticos Reificados . Y esto no es compatible con Java (aún? Fue planeado para Java 7, pero se ha pospuesto). En C #, por ejemplo, se admite suponiendo que T
tiene un constructor predeterminado . Incluso puede obtener el tipo de tiempo de ejecución por typeof(T)
y obtener los constructores por Type.GetConstructor()
. No hago C #, por lo que la sintaxis puede no ser válida, pero se ve más o menos así:
public class Foo<T> where T:new() {
public void foo() {
T t = new T();
}
}
La mejor "solución alternativa" para esto en Java es pasar un argumento de Class<T>
como método en su lugar, ya que varias respuestas ya se señalaron.
Lo que usted escribió no tiene ningún sentido, los genéricos en Java están destinados a agregar la funcionalidad del polimorfismo paramétrico a los objetos.
Qué significa eso? Significa que desea mantener algunas variables de tipo de sus clases indecisas, para poder usar sus clases con muchos tipos diferentes.
Pero su variable de tipo T
es un atributo que se resuelve en tiempo de ejecución, el compilador de Java compilará su clase demostrando seguridad de tipo sin intentar saber qué tipo de objeto es T
por lo que es imposible que use una variable de tipo en un método estático El tipo está asociado a una instancia en tiempo de ejecución del objeto, mientras que public void static main(..)
está asociada a la definición de la clase y en ese alcance T
no significa nada.
Si desea utilizar una variable de tipo dentro de un método estático, debe declarar el método como genérico (esto porque, como las variables de tipo explicado de una clase de plantilla están relacionadas con su instancia en tiempo de ejecución ), no la clase:
class SandBox
{
public static <T> void myMethod()
{
T foobar;
}
}
esto funciona, pero, por supuesto, no con el método main
, ya que no hay forma de llamarlo de forma genérica.
EDITAR : El problema es que debido a la borradura de tipo solo se compila una clase genérica y se pasa a JVM. El verificador de tipos simplemente verifica si el código es seguro, y luego, como demostró, se descarta todo tipo de información genérica .
Para instanciar T
, necesita saber el tipo de T
, pero puede tratarse de muchos tipos al mismo tiempo, por lo que una solución requiere solo la cantidad mínima de reflexión para usar Class<T>
para crear instancias de objetos nuevos:
public class SandBox<T>
{
Class<T> reference;
SandBox(Class<T> classRef)
{
reference = classRef;
}
public T getNewInstance()
{
try
{
return reference.newInstance();
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
public static void main(String[] args)
{
SandBox<String> t = new SandBox<String>(String.class);
System.out.println(t.getNewInstance().getClass().getName());
}
}
Por supuesto, esto implica que el tipo que desea instanciar:
- no es un tipo primitivo
- tiene un constructor por defecto
Para operar con diferentes tipos de constructores, debes profundizar en la reflexión.
Necesita obtener la información de tipo estáticamente. Prueba esto:
public class Abc<T> {
private Class<T> clazz;
public Abc(Class<T> clazz) {
this.clazz = clazz;
}
public T getInstanceOfT()
throws InstantiationException, IllegalAccessException {
return clazz.newInstance();
}
}
Úselo como tal:
Abc<String> abc = new Abc<String>(String.class);
abc.getInstanceOfT();
Dependiendo de sus necesidades, es posible que desee utilizar Class<? extends T>
Class<? extends T>
lugar.
Parece que está intentando crear la clase que sirve como punto de entrada a su aplicación como genérico, y eso no funcionará ... La JVM no sabrá qué tipo se supone que debe usar cuando se lo instancia como empiezas la aplicación
Sin embargo, si este fuera el caso más general, entonces algo así sería lo que estás buscando:
public MyGeneric<MyChoiceOfType> getMeAGenericObject(){
return new MyGeneric<MyChoiceOfType>();
}
o quizás:
MyGeneric<String> objMyObject = new MyGeneric<String>();
Parece que no entiendes cómo funcionan los genéricos. Es posible que desee consultar http://java.sun.com/j2se/1.5.0/docs/guide/language/generics.html Básicamente, lo que podría hacer es algo así como
public class Abc<T>
{
T someGenericThing;
public Abc(){}
public T getSomeGenericThing()
{
return someGenericThing;
}
public static void main(String[] args)
{
// create an instance of "Abc of String"
Abc<String> stringAbc = new Abc<String>();
String test = stringAbc.getSomeGenericThing();
}
}
Abc<String> abcInstance = new Abc<String> ();
..por ejemplo
public class Abc<T> {
public T getInstanceOfT(Class<T> aClass) {
return aClass.newInstance();
}
}
Tendrá que agregar manejo de excepciones.
Debe pasar el tipo real en el tiempo de ejecución, ya que no forma parte del código de bytes después de la compilación, por lo que no hay forma de conocerlo sin proporcionarlo explícitamente.