java - into - pass arraylist to array
Genéricos de Java en ArrayList.toArray() (8)
Puedo, y usaré un iterador en lugar de hacer una matriz a veces, pero esto siempre me pareció extraño. ¿Por qué el método get (x) sabe lo que está devolviendo, pero toArray () está predeterminado en Objetos? Es como a mitad de camino para diseñarlo, ¿decidieron que esto no era necesario aquí?
Como la intención de la pregunta parece no ser solo moverse usando
toArray()
con genéricos, sino también comprender el diseño de los métodos en la clase
ArrayList
, me gustaría agregar:
ArrayList
es una clase genérica, ya que se declara como
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
lo que hace posible utilizar métodos genéricos como
public E get(int index)
dentro de la clase.
Pero si un método como
toArray()
no devuelve
E
, sino
E[]
, las cosas comienzan a ponerse un poco complicadas.
No sería posible ofrecer una firma como
public <E> E[] toArray()
porque no es posible crear matrices genéricas.
La creación de matrices se produce en tiempo de ejecución y, debido a la
eliminación de tipo
, el tiempo de ejecución de Java no tiene información específica del tipo representado por
E
La única solución a partir de ahora es pasar el tipo requerido como parámetro al método y, por lo tanto, la firma
public <T> T[] toArray(T[] a)
donde los clientes se ven obligados a pasar el tipo requerido.
Pero, por otro lado, funciona para el
public E get(int index)
porque si observa la implementación del método, encontrará que a pesar de que el método utiliza la misma matriz de Object para devolver el elemento en el valor especificado índice, se lanza a
E
E elementData(int index) {
return (E) elementData[index];
}
Es el compilador de Java que en el momento de la compilación reemplaza
E
con
Object
Supongamos que tiene una lista de arrays definida de la siguiente manera:
ArrayList<String> someData = new ArrayList<>();
Más adelante en su código, debido a los genéricos, puede decir esto:
String someLine = someData.get(0);
Y el compilador sabe directamente que obtendrá una cadena. Yay genéricos! Sin embargo, esto fallará:
String[] arrayOfData = someData.toArray();
toArray()
siempre devolverá una matriz de objetos, no del genérico que se definió.
¿Por qué el método
get(x)
sabe lo que está devolviendo, pero
toArray()
predeterminado en Objetos?
Es posible crear una matriz "genérica" del tipo dado (conocido). Normalmente uso algo como esto en mi código.
public static <T> T[] toArray(Class<T> type, ArrayList<T> arrList) {
if ((arrList == null) || (arrList.size() == 0)) return null;
Object arr = Array.newInstance(type, arrList.size());
for (int i=0; i < arrList.size(); i++) Array.set(arr, i, arrList.get(i));
return (T[])arr;
}
La información genérica se
erased
en tiempo de ejecución.
JVM no sabe si su lista es
List<String>
o
List<Integer>
(en tiempo de ejecución
T
en
List<T>
se resuelve como
Object
), por lo que el único tipo de matriz posible es
Object[]
.
Sin
toArray(T[] array)
puede usar
toArray(T[] array)
; en este caso, JVM puede usar la clase de una matriz determinada, puede verla en la implementación de
ArrayList
:
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a''s runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
Lo pertinente a tener en cuenta es que las matrices en Java conocen su tipo de componente en tiempo de ejecución.
String[]
e
Integer[]
son clases diferentes en tiempo de ejecución, y puede solicitar a las matrices su tipo de componente en tiempo de ejecución.
Por lo tanto, se necesita un tipo de componente en tiempo de ejecución (ya sea codificando de forma rígida un tipo de componente reificable en tiempo de compilación con una
new String[...]
o utilizando
Array.newInstance()
y pasando un objeto de clase) para crear una matriz.
Por otro lado, los argumentos de tipo en genéricos no existen en tiempo de ejecución.
No hay absolutamente ninguna diferencia en tiempo de ejecución entre una
ArrayList<String>
y una
ArrayList<Integer>
.
Todo es solo
ArrayList
.
Esa es la razón fundamental por la que no puede simplemente tomar una
List<String>
y obtener una
String[]
sin pasar el tipo de componente por separado de alguna manera: tendría que obtener información del tipo de componente de algo que no tiene tipo de componente información.
Claramente, esto es imposible.
Lo primero que debe entender es que lo que posee
ArrayList
es solo una matriz de
Object
transient Object[] elementData;
Cuando se trata de la razón por la cual
T[]
falla, es porque no se puede obtener una matriz de tipo genérico sin una
Class<T>
y esto se debe a que borra el tipo de Java (
hay más explicaciones
y
cómo crear una
) .
Y la
array[]
en el montón conoce su tipo dinámicamente y no puede convertir
int[]
en
String[]
.
La misma razón, no puedes lanzar
Object[]
a
T[]
.
int[] ints = new int[3];
String[] strings = (String[]) ints;//java: incompatible types: int[] cannot be converted to java.lang.String[]
public <T> T[] a() {
Object[] objects = new Object[3];
return (T[])objects;
}
//ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
Integer[] a = new LearnArray().<Integer>a();
Pero lo que pones en la
array
es solo un objeto cuyo tipo es
E
(que es verificado por el compilador), por lo que puedes convertirlo a
E
que es seguro y correcto.
return (E) elementData[index];
En resumen, no puedes obtener lo que no tienes por elenco.
Solo tiene
Object[]
, por lo que
toArray()
puede devolver
Object[]
(de lo contrario, debe darle una
Class<T>
para hacer una nueva matriz con este tipo).
ArrayList<E>
E
en
ArrayList<E>
, puedes obtener una
E
con
get()
.
Si mira el
Javadoc para la interfaz de
List
, notará una segunda forma de
toArray
:
<T> T[] toArray(T[] a)
.
De hecho, el Javadoc incluso da un ejemplo de cómo hacer exactamente lo que quieres hacer:
String[] y = x.toArray(new String[0]);
Si observa la implementación de
toArray(T[] a)
de la clase
ArrayList<E>
, es como:
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a''s runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
El problema con este método es que necesita pasar una matriz del mismo tipo genérico. Ahora considere si este método no toma ningún argumento, entonces la implementación sería algo similar a:
public <T> T[] toArray() {
T[] t = new T[size]; // compilation error
return Arrays.copyOf(elementData, size, t.getClass());
}
Pero el problema aquí es que
no puede crear matrices genéricas en Java
porque el compilador no sabe exactamente qué representa
T
En otras palabras, la
creación de una matriz de un tipo no reificable
(
JLS §4.7
)
no está permitida en Java
.
Otra cita importante de Array Store Exception ( JLS §10.5 ):
Si el tipo de componente de una matriz no fuera reificable (§4.7), la Máquina virtual Java no podría realizar la verificación de la tienda descrita en el párrafo anterior. Es por eso que una expresión de creación de matriz con un tipo de elemento no reifiable está prohibida (§15.10.1).
Es por eso que Java ha proporcionado una versión sobrecargada a
toArray(T[] a)
.
Reemplazaré el método toArray () para decirle que devolverá una matriz de E.
Entonces, en lugar de anular
toArray()
, debe usar
toArray(T[] a)
.
No se pueden crear instancias de parámetros de tipo desde Java Doc también puede ser interesante para usted.
Una matriz es de un tipo diferente al tipo de matriz. Es una especie de clase StringArray en lugar de la clase String.
Suponiendo que sería posible, un método genérico para
toArray()
se vería así
private <T> T[] toArray() {
T[] result = new T[length];
//populate
return result;
}
Ahora durante la compilación, el tipo T se borra.
¿Cómo se debe reemplazar la parte
new T[length]
?
La información de tipo genérico no está disponible.
Si observa el código fuente de (por ejemplo)
ArrayList
, verá lo mismo.
El
toArray(T[] a)
llena la matriz dada (si el tamaño coincide) o crea una nueva matriz nueva usando el
tipo
de parámetro, que es el tipo de matriz del tipo genérico T.