convertir - Lista de Java<T> T[] toArray(T[] a) implementación
list to[] java (6)
Creo que es probable que dasblinkenlight tenga razón en que esto tiene algo que ver con generar un método existente, y la compatibilidad total es algo sutil de lograr.
El punto de beny23 también es muy bueno: el método debe aceptar supertipos de E[]
. Uno podría intentar
<T super E> T[] toArray(T[] a)
pero Java no permite super
en una variable de tipo, por falta de casos de uso :)
(edit: no, este no es un buen caso de uso para super
, consulte https://stackoverflow.com/a/2800425/2158288 )
Solo estaba mirando el método definido en la interfaz de la Lista: <T> T[] toArray(T[] a)
, y tengo una pregunta. ¿Por qué es genérico? Debido a este hecho, el método no es completamente seguro para el tipo. El siguiente fragmento de código compila, pero causa ArrayStoreException
:
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
String[] stringArray = list.toArray(new String[]{});
Me parece que si toArray no fuera genérico y tomara el parámetro de tipo Lista, sería mejor.
He escrito un ejemplo de juguete y está bien sin genérico:
package test;
import java.util.Arrays;
public class TestGenerics<E> {
private Object[] elementData = new Object[10];
private int size = 0;
public void add(E e) {
elementData[size++] = e;
}
@SuppressWarnings("unchecked")
//I took this code from ArrayList but it is not generic
public E[] toArray(E[] a) {
if (a.length < size)
// Make a new array of a''s runtime type, but my contents:
return (E[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
public static void main(String[] args) {
TestGenerics<Integer> list = new TestGenerics<Integer>();
list.add(1);
list.add(2);
list.add(3);
//You don''t have to do any casting
Integer[] n = new Integer[10];
n = list.toArray(n);
}
}
¿Hay alguna razón por la que se declara así?
De los javadocs :
Al igual que el método toArray (), este método actúa como puente entre las API basadas en matrices y basadas en colecciones. Además, este método permite un control preciso sobre el tipo de tiempo de ejecución de la matriz de salida y, en ciertas circunstancias, puede utilizarse para ahorrar costos de asignación.
Esto significa que el programador tiene el control sobre qué tipo de matriz debería ser.
Por ejemplo, para su ArrayList<Integer>
lugar de una matriz Integer[]
es posible que desee una matriz Number[]
u Object[]
.
Además, el método también comprueba la matriz que se pasa. Si pasa en una matriz que tiene suficiente espacio para todos los elementos, el método toArray
reutiliza esa matriz. Esto significa:
Integer[] myArray = new Integer[myList.size()];
myList.toArray(myArray);
o
Integer[] myArray = myList.toArray(new Integer[myList.size()]);
tiene el mismo efecto que
Integer[] myArray = myList.toArray(new Integer[0]);
Tenga en cuenta que, en versiones anteriores de Java, la última operación utilizó la reflexión para verificar el tipo de matriz y luego construir dinámicamente una matriz del tipo correcto. Al pasar una matriz de tamaño correcto en primer lugar, la reflexión no tuvo que usarse para asignar una nueva matriz dentro del método toArray
. Ese ya no es el caso, y ambas versiones se pueden usar indistintamente.
Es de tipo seguro: no causa una ClassCastException
. Eso es generalmente lo que significa seguridad de tipo.
ArrayStoreException
es diferente. Si incluye ArrayStoreException
en "no es seguro para el tipo", entonces todas las matrices en Java no son seguras para el tipo.
El código que usted publicó también produce ArrayStoreException
. Sólo inténtalo:
TestGenerics<Object> list = new TestGenerics<Object>();
list.add(1);
String[] n = new String[10];
list.toArray(n); // ArrayStoreException
De hecho, simplemente no es posible permitir que el usuario pase una matriz del tipo que desea obtener y, al mismo tiempo, no tenga la ArrayStoreException
. Porque cualquier firma de método que acepte una matriz de algún tipo también permite matrices de subtipos.
Entonces, como no es posible evitar la ArrayStoreException
, ¿por qué no hacerlo tan genérico como sea posible? ¿Para que el usuario pueda usar una matriz de algún tipo no relacionado si de alguna manera saben que todos los elementos serán instancias de ese tipo?
La razón por la cual este método es tal como es es en su mayoría histórico.
Existe una diferencia entre las clases genéricas y los tipos de matriz: mientras que los parámetros de tipo de la clase genérica se borran en tiempo de ejecución, el tipo de los elementos de las matrices no lo es. Entonces, en tiempo de ejecución, la JVM no ve ninguna diferencia entre la List<Integer>
y la List<String>
, ¡pero sí ve una diferencia entre el Integer[]
y la String[]
! La razón de esta diferencia es que las matrices siempre han estado allí, desde Java 1.0 en adelante, mientras que los genéricos solo se agregaron (de una manera compatible con versiones anteriores) en Java 1.5.
La API de colecciones se agregó en Java 1.2, antes de la introducción de los genéricos. En ese momento la interfaz de la List
ya contenía un método.
Object[] toArray(Object[] a);
(ver esta copia del 1.2 JavaDoc ). Esta fue la única forma de crear una matriz con un tipo de tiempo de ejecución especificado por el usuario: el parámetro a
sirvió como un token de tipo, es decir, determinó el tipo de tiempo de ejecución de la matriz devuelta (tenga en cuenta que si A
es una subclase de B
, A[]
se considera un subtipo de B[]
aunque List<A>
no es un subtipo de List<B>
).
Cuando se introdujeron los genéricos en Java 1.5, muchos métodos existentes se hicieron genéricos, y el método toArray
se convirtió en
<T> T[] toArray(T[] a);
que, después del borrado de tipo, tiene la misma firma que el método original no genérico.
La razón por la que el método tiene esta firma es porque la API toArray
es anterior a los genéricos: el método
public Object[] toArray(Object[] a)
Se ha introducido desde Java 1.2.
El genérico correspondiente que reemplaza a Object
con T
se ha introducido como una opción compatible con versiones anteriores:
public <T> T[] toArray(T[] a)
Cambiar la firma a genérico permite a las personas que llaman evitar el reparto: antes de Java 5, las personas que llamaban necesitaban hacer esto:
String[] arr = (String[])stringList.toArray(new String[stringList.size()]);
Ahora pueden hacer la misma llamada sin un reparto:
String[] arr = stringList.toArray(new String[stringList.size()]);
EDITAR:
Una firma más "moderna" para el método toArray
sería un par de sobrecargas:
public <T> T[] toArray(Class<T> elementType)
public <T> T[] toArray(Class<T> elementType, int count)
Esto proporcionaría una alternativa más expresiva e igualmente versátil a la firma del método actual. También hay una implementación eficiente de esto con el Array.newInstance(Class<T>,int)
implementado. Sin embargo, cambiar la firma de esta manera no sería compatible con versiones anteriores.
Se declara genéricamente para que pueda escribir código como
Integer[] intArray = list.toArray(new Integer[0]);
Sin lanzar el array regresando.
Se declara con la siguiente anotación:
@SuppressWarnings("unchecked")
En otras palabras, Java confía en que pase un parámetro de matriz del mismo tipo, por lo que su error no se produce.