Tengo un List<SubClass>
que quiero tratar como a List<BaseClass>
. Parece que no debería ser un problema ya que lanzar un SubClass
a a BaseClass
es muy fácil, pero mi compilador se queja de que el reparto es imposible.
Entonces, ¿cuál es la mejor manera de obtener una referencia a los mismos objetos que a List<BaseClass>
?
En este momento solo estoy haciendo una nueva lista y copiando la lista anterior:
List<BaseClass> convertedList = new ArrayList<BaseClass>(listOfSubClass)
Pero, según tengo entendido, eso tiene que crear una lista completamente nueva. ¡Me gustaría una referencia a la lista original, si es posible!
java
inheritance
casting
Riley Lark
fuente
fuente
Respuestas:
La sintaxis para este tipo de asignación utiliza un comodín:
Es importante darse cuenta de que a no
List<SubClass>
es intercambiable con a . El código que retiene una referencia al esperará que cada elemento de la lista sea a . Si otra parte del código se refiere a la lista como a , el compilador no se quejará cuando se inserte un o . Pero esto causará un primer fragmento de código, que supone que todo en la lista es un .List<BaseClass>
List<SubClass>
SubClass
List<BaseClass>
BaseClass
AnotherSubClass
ClassCastException
SubClass
Las colecciones genéricas no se comportan igual que las matrices en Java. Las matrices son covariantes; es decir, está permitido hacer esto:
Esto está permitido, porque la matriz "conoce" el tipo de sus elementos. Si alguien intenta almacenar algo que no es una instancia de
SubClass
la matriz (a través de labases
referencia), se generará 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 compilación. Por lo tanto, no pueden generar una excepción de tiempo de ejecución cuando se produce una tienda no válida. En su lugar, se
ClassCastException
generará un punto de código distante y difícil de asociar cuando se lea un valor de la colección. Si presta atención a las advertencias del compilador sobre la seguridad de tipos, evitará estos errores de tipo en tiempo de ejecución.fuente
Erickson ya explicó por qué no puede 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 tomando un
List<? extends BaseClass>
.Pero si no lo es y no puede cambiarlo, puede ajustar la lista
Collections.unmodifiableList(...)
, lo que permite devolver una Lista de un supertipo del parámetro del argumento. (Evita el problema de la seguridad de tipos al lanzar UnsupportedOperationException en los intentos de inserción).fuente
Como explicó @erickson, si realmente desea una referencia a la lista original, asegúrese de que ningún código inserte nada en esa lista si alguna vez quiere usarla nuevamente bajo su declaración original. La forma más sencilla de obtenerlo es lanzarlo a una lista simple y poco genérica:
No recomendaría esto si no sabe lo que le sucede a la Lista y le sugeriría que cambie cualquier código que necesite la Lista para aceptar la Lista que tiene.
fuente
A continuación se muestra un fragmento útil que funciona. Construye una nueva lista de matrices, pero la creación de objetos JVM por encima es insignificante.
Vi que otras respuestas son innecesariamente complicadas.
fuente
Perdí la respuesta en la que acabas de emitir la lista original, usando un doble elenco. Entonces aquí está para completar:
No se copia nada y la operación es rápida. Sin embargo, está engañando al compilador aquí, por lo que debe asegurarse absolutamente de no modificar la lista de tal manera que
subList
comience a contener elementos de un subtipo diferente. Cuando se trata de listas inmutables, esto generalmente no es un problema.fuente
List<BaseClass> convertedList = Collections.checkedList(listOfSubClass, BaseClass.class)
fuente
Lo que está tratando de hacer es muy útil y creo que necesito hacerlo muy a menudo en el código que escribo. Un ejemplo de caso de uso:
Digamos que tenemos una interfaz
Foo
y tenemos unzorking
paqueteZorkingFooManager
que crea y administra instancias de paquetes privadosZorkingFoo implements Foo
. (Un escenario muy común).Entonces,
ZorkingFooManager
necesita contener aprivate Collection<ZorkingFoo> zorkingFoos
pero necesita exponer apublic Collection<Foo> getAllFoos()
.La mayoría de los programadores de Java no pensarían dos veces antes de implementar
getAllFoos()
como asignar un nuevoArrayList<Foo>
, rellenarlo con todos los elementoszorkingFoos
y devolverlo. Me gusta entretener 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 recolectados de basura después de su creación.La solución a este problema es, por supuesto, reducir la colección. Aquí está la mejor manera de hacerlo:
Lo que nos lleva a la
castList()
función:La
result
variable intermedia es necesaria debido a una perversión del lenguaje java:return (List<T>)list;
produce una excepción de "lanzamiento no verificado"; Hasta aquí todo bien; pero entonces:@SuppressWarnings( "unchecked" ) return (List<T>)list;
es un uso ilegal de la anotación suprimir advertencias.Entonces, aunque no es kosher usarlo
@SuppressWarnings
en unareturn
declaración, aparentemente está bien usarlo en una tarea, por lo que la variable adicional "resultado" resuelve este problema. (De todos modos, debe ser optimizado por el compilador o por el JIT).fuente
Algo como esto también debería funcionar:
Realmente no sé por qué se necesita clazz en Eclipse.
fuente
¿Qué hay de lanzar todos los elementos? Creará una nueva lista, pero hará referencia a los objetos originales de la lista anterior.
fuente
Este es el código de trabajo completo que utiliza Generics, para enviar la lista de subclases a superclase.
Método de llamada que pasa el tipo de subclase
Método Callee que acepta cualquier subtipo de la clase base
fuente