metodos instanciar genericos genericas generica comparar clases arreglos java arrays generics reflection instantiation

instanciar - ¿Cómo crear una matriz genérica en Java?



list generica java (29)

¿Qué pasa con esta solución?

@SafeVarargs public static <T> T[] toGenericArray(T ... elems) { return elems; }

Funciona y parece demasiado simple para ser verdad. ¿Hay algún inconveniente?

Debido a la implementación de los genéricos de Java, no puede tener un código como este:

public class GenSet<E> { private E a[]; public GenSet() { a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation } }

¿Cómo puedo implementar esto mientras mantengo la seguridad de tipos?

Vi una solución en los foros de Java que es así:

import java.lang.reflect.Array; class Stack<T> { public Stack(Class<T> clazz, int capacity) { array = (T[])Array.newInstance(clazz, capacity); } private final T[] array; }

Pero realmente no entiendo lo que está pasando.


A continuación, le indicamos cómo usar los genéricos para obtener una matriz del tipo exacto que está buscando y al mismo tiempo preservar la seguridad de los tipos (a diferencia de las otras respuestas, que le devolverán una matriz de Object o generarán advertencias en el momento de la compilación):

import java.lang.reflect.Array; public class GenSet<E> { private E[] a; public GenSet(Class<E[]> clazz, int length) { a = clazz.cast(Array.newInstance(clazz.getComponentType(), length)); } public static void main(String[] args) { GenSet<String> foo = new GenSet<String>(String[].class, 1); String[] bar = foo.a; foo.a[0] = "xyzzy"; String baz = foo.a[0]; } }

Eso compila sin advertencias, y como puede ver en main , para cualquier tipo de declaración de GenSet , puede asignar a a una matriz de ese tipo, y puede asignar un elemento de a a una variable de ese tipo, lo que significa que la matriz y los valores en la matriz son del tipo correcto.

Funciona utilizando literales de clase como tokens de tipo de tiempo de ejecución, como se explica en los Tutoriales de Java . Los literales de clase son tratados por el compilador como instancias de java.lang.Class . Para usar uno, simplemente sigue el nombre de una clase con .class . Por lo tanto, String.class actúa como un objeto de Class que representa la clase String . Esto también funciona para interfaces, enumeraciones, matrices de cualquier dimensión (por ejemplo, String[].class ), primitivos (por ejemplo, int.class ) y la palabra clave void (es decir, void.class ).

Class sí es genérica (declarada como Class<T> , donde T representa el tipo que representa el objeto de Class ), lo que significa que el tipo de String.class es Class<String> .

Por lo tanto, siempre que llame al constructor para GenSet , pase un literal de clase para el primer argumento que representa una matriz del tipo declarado de la instancia de GenSet (por ejemplo, String[].class for GenSet<String> ). Tenga en cuenta que no podrá obtener una matriz de primitivas, ya que las primitivas no se pueden usar para variables de tipo.

Dentro del constructor, llamar al método cast devuelve el argumento Object pasado convertido a la clase representada por el objeto Class en el que se llamó al método. Llamar al método estático newInstance en java.lang.reflect.Array devuelve como un Object una matriz del tipo representado por el Objeto de Class pasado como el primer argumento y de la longitud especificada por el int pasado como el segundo argumento. Al llamar al método getComponentType devuelve un objeto Class que representa el tipo de componente de la matriz representada por el objeto Class en el que se llamó al método (por ejemplo, String.class para String[].class , null si el objeto Class no representa una matriz) .

La última frase no es del todo precisa. Calling String[].class.getComponentType() devuelve un objeto Class que representa la clase String , pero su tipo es Class<?> , No Class<String> , por lo que no puede hacer algo como lo siguiente.

String foo = String[].class.getComponentType().cast("bar"); // won''t compile

Lo mismo ocurre con cada método en Class que devuelve un objeto Class .

Con respecto al comentario de Joachim Sauer sobre esta respuesta (no tengo suficiente reputación como para comentarlo yo mismo), el ejemplo que usa la conversión a T[] resultará en una advertencia porque el compilador no puede garantizar la seguridad del tipo en ese caso.

Editar sobre los comentarios de Ingo:

public static <T> T[] newArray(Class<T[]> type, int size) { return type.cast(Array.newInstance(type.getComponentType(), size)); }


De hecho, encontré una solución bastante única para evitar la incapacidad de iniciar una matriz genérica. Lo que tienes que hacer es crear una clase que tome la variable genérica T así:

class GenericInvoker <T> { T variable; public GenericInvoker(T variable){ this.variable = variable; } }

y luego en tu clase de matriz, haz que comience así:

GenericInvoker<T>[] array; public MyArray(){ array = new GenericInvoker[]; }

iniciar un new Generic Invoker[] causará un problema si no se selecciona, pero en realidad no debería haber ningún problema.

Para obtener de la matriz, debe llamar a la matriz [i] .variable así:

public T get(int index){ return array[index].variable; }

El resto, como cambiar el tamaño de la matriz, se puede hacer con Arrays.copyOf () de esta forma:

public void resize(int newSize){ array = Arrays.copyOf(array, newSize); }

Y la función de agregar se puede agregar así:

public boolean add(T element){ // the variable size below is equal to how many times the add function has been called // and is used to keep track of where to put the next variable in the array arrays[size] = new GenericInvoker(element); size++; }


El ejemplo está utilizando la reflexión de Java para crear una matriz. En general, no se recomienda hacer esto, ya que no es de tipo seguro. En su lugar, lo que debe hacer es utilizar una Lista interna y evitar la matriz.


El elenco forzado sugerido por otras personas no funcionó para mí, lanzando una excepción de casting ilegal.

Sin embargo, este elenco implícito funcionó bien:

Item<K>[] array = new Item[SIZE];

donde Item es una clase que definí que contiene el miembro:

private K value;

De esta manera obtendrá una matriz de tipo K (si el elemento solo tiene el valor) o cualquier tipo genérico que desee que se defina en la clase Artículo.


En Java 8, podemos hacer una especie de creación de matriz genérica utilizando una lambda o una referencia de método. Esto es similar al enfoque reflexivo (que pasa una Class ), pero aquí no estamos utilizando la reflexión.

@FunctionalInterface interface ArraySupplier<E> { E[] get(int length); } class GenericSet<E> { private final ArraySupplier<E> supplier; private E[] array; GenericSet(ArraySupplier<E> supplier) { this.supplier = supplier; this.array = supplier.get(10); } public static void main(String[] args) { GenericSet<String> ofString = new GenericSet<>(String[]::new); GenericSet<Double> ofDouble = new GenericSet<>(Double[]::new); } }

Por ejemplo, esto lo usa <A> A[] Stream.toArray(IntFunction<A[]>) .

Esto también podría hacerse antes de Java 8 usando clases anónimas, pero es más engorroso.


En realidad, una forma más fácil de hacerlo es crear una matriz de objetos y convertirlos en el tipo deseado, como en el siguiente ejemplo:

T[] array = (T[])new Object[SIZE];

donde SIZE es una constante y T es un identificador de tipo


Encontré una especie de trabajo alrededor de este problema.

La siguiente línea arroja un error de creación de matriz genérica

List<Person>[] personLists=new ArrayList<Person>()[10];

Sin embargo, si encapsulo List<Person> en una clase separada, funciona.

import java.util.ArrayList; import java.util.List; public class PersonList { List<Person> people; public PersonList() { people=new ArrayList<Person>(); } }

Puede exponer a las personas de la clase Lista personal a través de un captador. La línea a continuación le dará una matriz, que tiene una List<Person> en cada elemento. En otras palabras, array of List<Person> .

PersonList[] personLists=new PersonList[10];

Necesitaba algo como esto en algún código en el que estaba trabajando y esto es lo que hice para que funcionara. Hasta ahora no hay problemas.


Esta es la única respuesta que es de tipo seguro.

E[] a; a = newArray(size); @SafeVarargs static <E> E[] newArray(int length, E... array) { return Arrays.copyOf(array, length); }


Esto se trata en el Capítulo 5 (Genéricos) de Effective Java, 2nd Edition , item 25 ... Prefiera las listas a los arreglos

Su código funcionará, aunque generará una advertencia sin marcar (que podría suprimir con la siguiente anotación:

@SuppressWarnings({"unchecked"})

Sin embargo, probablemente sería mejor usar una lista en lugar de una matriz.

Hay una discusión interesante sobre este error / característica en el sitio del proyecto OpenJDK .


He encontrado una manera rápida y fácil que funciona para mí. Tenga en cuenta que solo he usado esto en Java JDK 8. No sé si funcionará con versiones anteriores.

Aunque no podemos crear una instancia de una matriz genérica de un parámetro de tipo específico, podemos pasar una matriz ya creada a un constructor de clase genérico.

class GenArray <T> { private T theArray[]; // reference array // ... GenArray(T[] arr) { theArray = arr; } // Do whatever with the array... }

Ahora en main podemos crear el array así:

class GenArrayDemo { public static void main(String[] args) { int size = 10; // array size // Here we can instantiate the array of the type we want, say Character (no primitive types allowed in generics) Character[] ar = new Character[size]; GenArray<Character> = new Character<>(ar); // create the generic Array // ... } }

Para mayor flexibilidad con sus arreglos, puede usar una lista enlazada, por ejemplo. ArrayList y otros métodos que se encuentran en la clase Java.util.ArrayList.


Hice este fragmento de código para crear una instancia reflexiva de una clase que se pasa por una sencilla utilidad de prueba automatizada.

Object attributeValue = null; try { if(clazz.isArray()){ Class<?> arrayType = clazz.getComponentType(); attributeValue = Array.newInstance(arrayType, 0); } else if(!clazz.isInterface()){ attributeValue = BeanUtils.instantiateClass(clazz); } } catch (Exception e) { logger.debug("Cannot instanciate /"{}/"", new Object[]{clazz}); }

Tenga en cuenta este segmento:

if(clazz.isArray()){ Class<?> arrayType = clazz.getComponentType(); attributeValue = Array.newInstance(arrayType, 0); }

para iniciar la matriz donde Array.newInstance (clase de matriz, tamaño de la matriz) . La clase puede ser primitiva (int.class) y objeto (Integer.class).

BeanUtils es parte de la primavera.


Hola, aunque el hilo está muerto, me gustaría llamar su atención sobre esto:

Los genéricos se utilizan para la verificación de tipos durante el tiempo de compilación:

  • Por lo tanto, el propósito es comprobar que lo que entra es lo que necesita.
  • Lo que devuelves es lo que necesita el consumidor.
  • Mira esto:

No se preocupe por escribir advertencias cuando esté escribiendo una clase genérica. Preocúpate cuando lo estés usando.


Los genéricos de Java funcionan al verificar los tipos en el momento de la compilación e insertando las conversiones apropiadas, pero borrando los tipos en los archivos compilados. Esto hace que las bibliotecas genéricas sean utilizables por un código que no comprende los genéricos (que fue una decisión de diseño deliberada), pero que por lo general no puede averiguar cuál es el tipo en el tiempo de ejecución.

El constructor público de Stack(Class<T> clazz,int capacity) requiere que pase un objeto de clase en tiempo de ejecución, lo que significa que la información de clase está disponible en tiempo de ejecución para el código que la necesita. Y el formulario Class<T> significa que el compilador comprobará que el objeto Clase que pasa es precisamente el objeto Clase para el tipo T. No es una subclase de T, no es una superclase de T, sino precisamente T.

Esto significa que puede crear un objeto de matriz del tipo apropiado en su constructor, lo que significa que el tipo de los objetos que almacena en su colección tendrá sus tipos verificados en el punto en que se agregan a la colección.


Me pregunto si este código crearía una matriz genérica efectiva?

public T [] createArray(int desiredSize){ ArrayList<T> builder = new ArrayList<T>(); for(int x=0;x<desiredSize;x++){ builder.add(null); } return builder.toArray(zeroArray()); } //zeroArray should, in theory, create a zero-sized array of T //when it is not given any parameters. private T [] zeroArray(T... i){ return i; }

Edición: ¿Quizás una forma alternativa de crear una matriz de este tipo, si se conociera el tamaño requerido y fuera pequeño, sería simplemente ingresar el número requerido de "null" en el comando zeroArray?

Aunque obviamente esto no es tan versátil como usar el código createArray.


Mira también a este código:

public static <T> T[] toArray(final List<T> obj) { if (obj == null || obj.isEmpty()) { return null; } final T t = obj.get(0); final T[] res = (T[]) Array.newInstance(t.getClass(), obj.size()); for (int i = 0; i < obj.size(); i++) { res[i] = obj.get(i); } return res; }

Convierte una lista de cualquier tipo de objeto en una matriz del mismo tipo.


Nadie más ha respondido la pregunta de lo que está pasando en el ejemplo que publicaste.

import java.lang.reflect.Array; class Stack<T> { public Stack(Class<T> clazz, int capacity) { array = (T[])Array.newInstance(clazz, capacity); } private final T[] array; }

Como otros han dicho, los genéricos se "borran" durante la compilación. Entonces, en tiempo de ejecución, una instancia de un genérico no sabe cuál es su tipo de componente. La razón de esto es histórica, Sun quería agregar genéricos sin romper la interfaz existente (tanto de origen como binario).

Las matrices, por otro lado , conocen su tipo de componente en el tiempo de ejecución.

Este ejemplo resuelve el problema haciendo que el código que llama al constructor (que conoce el tipo) pase un parámetro que le diga a la clase el tipo requerido.

Así que la aplicación construiría la clase con algo como

Stack<foo> = new Stack<foo>(foo.class,50)

y el constructor ahora sabe (en tiempo de ejecución) cuál es el tipo de componente y puede usar esa información para construir la matriz a través de la API de reflexión.

Array.newInstance(clazz, capacity);

Finalmente, tenemos un tipo de conversión porque el compilador no tiene forma de saber que la matriz devuelta por Array#newInstance() es el tipo correcto (aunque lo sepamos).

Este estilo es un poco feo, pero a veces puede ser la solución menos mala para crear tipos genéricos que necesitan conocer su tipo de componente en tiempo de ejecución por cualquier motivo (crear matrices o crear instancias de su tipo de componente, etc.).


No es necesario pasar el argumento de clase al constructor. Prueba esto.

static class GenSet<T> { private final T[] array; @SuppressWarnings("unchecked") public GenSet(int capacity, T... dummy) { if (dummy.length > 0) throw new IllegalArgumentException( "Do not provide values for dummy argument."); Class<?> c = dummy.getClass().getComponentType(); array = (T[])Array.newInstance(c, capacity); } @Override public String toString() { return "GenSet of " + array.getClass().getComponentType().getName() + "[" + array.length + "]"; } }

y

GenSet<Integer> intSet = new GenSet<>(3); System.out.println(intSet); System.out.println(new GenSet<String>(2));

resultado:

GenSet of java.lang.Integer[3] GenSet of java.lang.String[2]


Para ampliar a más dimensiones, simplemente agregue los parámetros de [] y dimensión a newInstance() ( T es un parámetro de tipo, cls es una Class<T> , d1 a d5 son enteros):

T[] array = (T[])Array.newInstance(cls, d1); T[][] array = (T[][])Array.newInstance(cls, d1, d2); T[][][] array = (T[][][])Array.newInstance(cls, d1, d2, d3); T[][][][] array = (T[][][][])Array.newInstance(cls, d1, d2, d3, d4); T[][][][][] array = (T[][][][][])Array.newInstance(cls, d1, d2, d3, d4, d5);

Vea Array.newInstance() para detalles.


Pasando una lista de valores ...

public <T> T[] array(T... values) { return values; }


Podría crear una matriz de objetos y convertirla en E en todas partes. Sí, no es una forma muy limpia de hacerlo, pero al menos debería funcionar.


Podrías usar un molde:

public class GenSet<Item> { private Item[] a; public GenSet(int s) { a = (Item[]) new Object[s]; } }


Puede que no esté relacionado con esta pregunta, pero mientras obtenía el error de " generic array creation " para usar

Tuple<Long,String>[] tupleArray = new Tuple<Long,String>[10];

@SuppressWarnings({"unchecked"}) los siguientes trabajos (y trabajé para mí) con @SuppressWarnings({"unchecked"}) :

Tuple<Long, String>[] tupleArray = new Tuple[10];


Siempre puedes hacer esto:

E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];

Esta es una de las formas sugeridas de implementar una colección genérica en Effective Java; Artículo 26 . No hay errores de tipo, no hay necesidad de convertir la matriz repetidamente. Sin embargo, esto activa una advertencia porque es potencialmente peligroso y debe usarse con precaución. Como se detalla en los comentarios, este Object[] ahora se enmascara como nuestro tipo E[] , y puede causar errores inesperados o ClassCastException s si se usa de forma poco segura.

Como regla general, este comportamiento es seguro siempre que la matriz de conversión se use internamente (por ejemplo, para respaldar una estructura de datos) y no se devuelva ni se exponga al código del cliente. Si necesita devolver una matriz de un tipo genérico a otro código, la clase de Array reflexión que menciona es la manera correcta de hacerlo.

Vale la pena mencionar que siempre que sea posible, tendrá un tiempo mucho más feliz trabajando con List s en lugar de matrices si está usando genéricos. Ciertamente, a veces no tienes otra opción, pero usar el marco de colecciones es mucho más sólido.


Tengo que hacer una pregunta a cambio: ¿su GenSet "comprobado" o "no seleccionado"? Qué significa eso?

  • Comprobado : mecanografía fuerte . GenSet sabe explícitamente qué tipo de objetos contiene (es decir, su constructor fue llamado explícitamente con un argumento de Class<E> , y los métodos lanzarán una excepción cuando se pasen argumentos que no son del tipo E Ver Collections.checkedCollection .

    -> En ese caso, deberías escribir:

    public class GenSet<E> { private E[] a; public GenSet(Class<E> c, int s) { // Use Array native method to create array // of a type only known at run time @SuppressWarnings("unchecked") final E[] a = (E[]) Array.newInstance(c, s); this.a = a; } E get(int i) { return a[i]; } }

  • Sin marcar : escritura débil . No se realiza ninguna comprobación de tipos en ninguno de los objetos pasados ​​como argumento.

    -> en ese caso, deberías escribir

    public class GenSet<E> { private Object[] a; public GenSet(int s) { a = new Object[s]; } E get(int i) { @SuppressWarnings("unchecked") final E e = (E) a[i]; return e; } }

    Tenga en cuenta que el tipo de componente de la matriz debe ser el erasure del parámetro de tipo:

    public class GenSet<E extends Foo> { // E has an upper bound of Foo private Foo[] a; // E erases to Foo, so use Foo[] public GenSet(int s) { a = new Foo[s]; } ... }

Todo esto se debe a una debilidad conocida y deliberada de los genéricos en Java: se implementó mediante el borrado, por lo que las clases "genéricas" no saben con qué tipo de argumento se crearon en el tiempo de ejecución y, por lo tanto, no pueden proporcionar el tipo. seguridad a menos que se implemente algún mecanismo explícito (verificación de tipo).


Una solución fácil, aunque complicada para esto, sería anidar una segunda clase "titular" dentro de su clase principal y usarla para mantener sus datos.

public class Whatever<Thing>{ private class Holder<OtherThing>{ OtherThing thing; } public Holder<Thing>[] arrayOfHolders = new Holder<Thing>[10] }


prueba esto.

private int m = 0; private int n = 0; private Element<T>[][] elements = null; public MatrixData(int m, int n) { this.m = m; this.n = n; this.elements = new Element[m][n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { this.elements[i][j] = new Element<T>(); } } }


La creación de matrices genéricas no está permitida en Java, pero puede hacerlo como

class Stack<T> { private final T[] array; public Stack(int capacity) { array = (T[]) new Object[capacity]; } }


private E a[]; private int size; public GenSet(int elem) { size = elem; a = (E[]) new E[size]; }