2011-09-02 22 views
5

Tengo un método que utiliza los varargs Característica:¿Cómo puedo pasar los contenidos de una lista a un método varargs?

void add(Animal ...); 

Ahora, en vez de hacer .add(dog, cat), tengo una lista de animales con número indeterminado de elementos,

List<Animal> i = new ArrayList<Animal>(); 
i.add(dog); 
i.add(cat); 

y quiere llamar a añadir con los elementos de esta lista.

Creo que podría utilizar una matriz, pero cuando lo hago .add(i.toArray()), da un error de compilación.

¿Cuál es la forma correcta de hacerlo?

+0

No entiendo la pregunta. Hiciste tu propio método 'Agregar (Animal ...)' pero estás llamando ArrayList.add (Objeto) y no el tuyo. ¿Porqué es eso? Por favor, edite la pregunta con el contexto del código. ¿Ese bloque de código está dentro de tu método'add'? Si su único problema es que no puede lanzar Object [] a Animal [], siga @Tom answer. – aalku

+0

Solo una nota para el futuro: si tiene un error de compilación, copie el mensaje de error completo y también las líneas de código fuente relevantes para este mensaje. –

Respuesta

10

Es:

add(i.toArray(new Animal[i.size()])) 

List.toArray devuelve un Object[], independientemente del argumento del tipo en la lista: a pesar de que se podría escribir new List<String>().toArray(), obtendrá una Object[]. Sin embargo, el version of toArray that takes an array to fill devuelve una matriz con el tipo correcto: si escribe new List<String>().toArray(new String[0]), obtendrá un String[]. Tenga en cuenta que el tamaño de la matriz que ingresa ni siquiera tiene que coincidir con el tamaño de la lista, aunque es una buena práctica asegurarse de que así sea.

Esto se debe a una característica ligeramente delicada de los genéricos. A primera vista, puede pensar que String[] y List<String> significan cosas similares para sus tipos de base: una es una matriz de cadenas y la otra es una lista de cadenas.

Sin embargo, de hecho son muy diferentes.

Una matriz es una primitiva del lenguaje, y tiene su tipo cocido en ella. Si tomó un editor hexadecimal y miró una instancia de matriz en la memoria en la JVM, podría encontrar (en algún lugar cercano) un registro del tipo de objetos que contiene. Eso significa que si toma una instancia de una matriz de algún tipo de componente desconocido, puede find out what that type is. Por el contrario, significa que si va al create an instance of an array, necesita saber qué tipo de componente desea.

El List, por el contrario, utiliza los genéricos, que en Java se implementa con type erasure, lo que significa que, en términos generales, es algo que existe en el compilador, pero no en tiempo de ejecución (el compilador pueda verificar que se hazlo bien, pero la JVM no puede). Esto lleva a una implementación simple y eficiente (una lo suficientemente simple como para haber sido agregada a Java pregenéricos sin cambiar la JVM), pero tiene algunas deficiencias, en particular, que en el tiempo de ejecución, no hay forma de saber cuál es el argumento de tipo en cualquier instancia particular de una clase genérica es, porque los argumentos de tipo solo existen en el compilador. Debido a que depende de la instancia List manejar toArray(), lo único que puede hacer es crear un Object[]. Simplemente no sabe de un tipo más específico para usar.

Una forma de ver esto es que las matrices tienen un argumento de tipo como parte de su clase, mientras que List s tienen un argumento de tipo como parte de su tipo, y puesto que los objetos tienen clases pero las variables tienen tipos, no puede obtener el argumento de tipo de un List de un objeto, solo de una variable que contiene un objeto (como un lado, tampoco puede obtener el argumento de tipo de una matriz de una variable que contiene una matriz (considere Object[] array = new String[0];), pero eso realmente no importa porque, la variable le permite agarrar un objeto, a menos que sea nulo).

Hervir esto a código, el problema es:

public <E> E[] createSimilarlyTypedArray(List<E> list) { 
    Class<E> componentType = list.???; // there is no way to do this 
    return Arrays.newInstance(componentType, list.size()); 
} 
+0

Utilizará su matriz si es lo suficientemente grande o asignará una nueva si no lo es. – aalku

+0

¿no recibirá una excepción de tipo de letra cuando llame a i.toArray (nuevo Animal []) en una lista que no tenga un tipo genérico establecido? –

+1

@Benjamin: no, no lo harás. Intenta ejecutar 'Arrays. asList ("hello"). ToArray (new String [1]); '- funciona perfectamente. Estáticamente, esto está bien porque esa versión de 'toArray' toma su parámetro tipo de su parámetro, no de la' Lista' propietaria (¡raro pero correcto!), Y dinámicamente, está bien porque en tiempo de ejecución, el tipo 'List' ha sido borrado –

0

cuando lo haga .add (i.toArray()) que da un error, lo que es la forma correcta de hacerlo ?

Utilice foo.addAll(i) y luego convierta foo en una matriz si es necesario.

0

Su método void add(Animal...) espera un objeto de la clase Animal o una matriz con objetos animales en él. Le das una matriz con objetos de la clase Object. Dar la lista un tipo genérico de este modo:

List<Animal> animals = new ArrayList<Animal>(); 
animals.add(dog); 
animals.add(cat) 

continuación, analizar la lista como argumento, mientras que la conversión a una matriz, a su método de este modo:

add(animals.toArray(new Animal[animals.size()]); 

Más sobre los genéricos se pueden encontrar en Java API

http://download.oracle.com/javase/1,5.0/docs/guide/language/generics.html

+1

Más que esperar "una matriz con objetos' Animal' en ella ", espera una matriz cuyo tipo es' Animal [] '. La forma de argumento cero de 'toArray' crea una matriz con objetos' Animal' en ella, una matriz que es básicamente 'new Object [] {new Animal()}'. Sin embargo, lo que se necesita es 'nuevo Animal [] {animal nuevo()}'. ¡La confluencia entre arrays y genéricos es un parche de agua realmente traicionero! –

Cuestiones relacionadas