La forma más eficiente para convertir List a List

Tengo una List que quiero tratar como una List . Parece que no debería ser un problema, ya que SubClass una SubClass a una BaseClass es muy fácil, pero mi comstackdor se queja de que el lanzamiento es imposible.

Entonces, ¿cuál es la mejor manera de obtener una referencia a los mismos objetos que List ?

Ahora mismo estoy haciendo una nueva lista y copiando la lista anterior:

 List convertedList = new ArrayList(listOfSubClass) 

Pero, según tengo entendido, tiene que crear una lista completamente nueva. Me gustaría una referencia a la lista original, si es posible.

La syntax para este tipo de asignación usa un comodín:

 List subs = ...; List bases = subs; 

Es importante darse cuenta de que una List no es intercambiable con una List . El código que conserva una referencia a la List esperará que cada elemento de la lista sea una SubClass . Si otra parte del código se refiere a la lista como List , el comstackdor no se quejará cuando se BaseClass una BaseClass u AnotherSubClass . Pero esto causará una ClassCastException para la primera pieza de código, que asume que todo en la lista es una SubClass .

Las colecciones genéricas no se comportan igual que las matrices en Java. Las matrices son covariantes; es decir, está permitido hacer esto:

 SubClass[] subs = ...; BaseClass[] bases = subs; 

Esto está permitido, porque la matriz “conoce” el tipo de sus elementos. Si alguien intenta almacenar algo que no es una instancia de SubClass en la matriz (a través de la referencia de bases ), se lanzará una excepción de tiempo de ejecución.

Las colecciones genéricas no “conocen” su tipo de componente; esta información se “borra” en el momento de la comstackción. Por lo tanto, no pueden generar una excepción de tiempo de ejecución cuando se produce un almacén no válido. En su lugar, se generará una ClassCastException en algún punto distante y difícil de asociar en el código cuando se lea un valor de la colección. Si presta atención a las advertencias del comstackdor sobre la seguridad del tipo, evitará estos errores de tipo en el tiempo de ejecución.

Erickson ya explicó por qué no puedes hacer esto, pero aquí hay algunas soluciones:

Si solo desea eliminar elementos de su lista base, en principio, su método de recepción debe declararse como una List List .

Pero si no lo es y no puede cambiarlo, puede ajustar la lista con Collections.unmodifiableList(...) , que permite devolver una Lista de un supertipo del parámetro del argumento. (Evita el problema de seguridad tipo lanzando UnsupportedOperationException en bashs de inserción).

Como explicó @erickson, si realmente quieres una referencia a la lista original, asegúrate de que ningún código inserte nada en esa lista si alguna vez deseas volver a usarla en su statement original. La forma más sencilla de obtenerlo es simplemente enviarlo a una simple lista no genérica antigua:

 List baseList = (List)new ArrayList(); 

No recomendaría esto si no sabe qué le sucede a la Lista y sugeriría que cambie el código que necesita la Lista para aceptar la Lista que tiene.

List convertedList = Collections.checkedList(listOfSubClass, BaseClass.class)

Lo que intenta hacer es muy útil y creo que tengo que hacerlo muy a menudo en el código que escribo. Un caso de uso de ejemplo:

Digamos que tenemos una interfaz Foo y tenemos un paquete zorking que tiene un ZorkingFooManager que crea y administra instancias de paquete privado. ZorkingFoo implements Foo . (Un escenario muy común)

Por lo tanto, ZorkingFooManager debe contener una private Collection zorkingFoos pero debe exponer una public Collection getAllFoos() .

La mayoría de los progtwigdores de Java no lo pensarían dos veces antes de implementar getAllFoos() como asignar un nuevo ArrayList , zorkingFoos con todos los elementos de zorkingFoos y devolverlo. Disfruto entreteniendo la idea de que aproximadamente el 30% de todos los ciclos de reloj consumidos por el código java que se ejecuta en millones de máquinas en todo el planeta no hace más que crear copias inútiles de ArrayLists que son microsegundos recogidos basura después de su creación.

La solución a este problema es, por supuesto, bajar la colección. Esta es la mejor manera de hacerlo:

 static  List downCastList( List list ) { return castList( list ); } 

Lo que nos lleva a la función castList() :

 static  List castList( List list ) { @SuppressWarnings( "unchecked" ) List result = (List)list; return result; } 

La variable de result intermedia es necesaria debido a una perversión del lenguaje Java:

  • return (List)list; produce una excepción de “lanzamiento sin verificar”; Hasta aquí todo bien; pero entonces:

  • @SuppressWarnings( "unchecked" ) return (List)list; es un uso ilegal de la anotación suprimir advertencias.

Por lo tanto, aunque no es kosher utilizar @SuppressWarnings en una statement de return , aparentemente está bien usarlo en una tarea, por lo que la variable adicional “resultado” resuelve este problema. (Debe ser optimizado por el comstackdor o por el JIT de todos modos).

Algo como esto debería funcionar también:

 public static  List convertListWithExtendableClasses( final List< ? extends T> originalList, final Class clazz ) { final List newList = new ArrayList<>(); for ( final T item : originalList ) { newList.add( item ); }// for return newList; } 

Realmente no sé por qué clazz se necesita en Eclipse …

¿Qué hay de lanzar todos los elementos. Creará una nueva lista, pero hará referencia a los objetos originales de la lista anterior.

 List convertedList = listOfSubClass.map(x -> (BaseClass)x).collect(Collectors.toList()); 

Esta es la pieza de código de trabajo completa que usa Generics, para convertir la lista de subclase en superclase.

Método de llamada que pasa el tipo de subclase

 List subClassParam = new ArrayList<>(); getElementDefinitionStatuses(subClassParam); 

Método de Callee que acepta cualquier subtipo de la clase base

 private static List getElementDefinitionStatuses(List baseClassVariableName) { return allElementStatuses; } } 

Debajo hay un fragmento útil que funciona. Construye una nueva lista de arreglos, pero la creación de objetos JVM sobre la cabeza es significativa.

Vi que otras respuestas son innecesariamente complicadas.

 List baselist = new ArrayList<>(sublist);