Por ejemplo, supongamos que tiene dos clases:
public class TestA {}
public class TestB extends TestA{}
Tengo un método que devuelve ay List<TestA>
me gustaría convertir todos los objetos en esa lista para TestB
que termine con a List<TestB>
.
Simplemente echando a List<TestB>
casi obras; pero no funciona porque no puede emitir un tipo genérico de un parámetro a otro. Sin embargo, puede emitir a través de un tipo de comodín intermedio y se permitirá (ya que puede emitir desde y hacia tipos de comodín, solo con una advertencia sin marcar):
List<TestB> variable = (List<TestB>)(List<?>) collectionOfListA;
La conversión de genéricos no es posible, pero si define la lista de otra manera, es posible almacenar TestB en ella:
Todavía tiene que hacer una verificación de tipo cuando usa los objetos de la lista.
fuente
Cannot instantiate the type ArrayList<? extends TestA>
.Realmente no puedes *:
Se toma un ejemplo de este tutorial de Java
Suponga que hay dos tipos
A
yB
tal queB extends A
. Entonces el siguiente código es correcto:El código anterior es válido porque
B
es una subclase deA
. Ahora, ¿qué pasa conList<A>
yList<B>
?Resulta que
List<B>
no es una subclase de,List<A>
por lo tanto, no podemos escribirAdemás, ni siquiera podemos escribir
*: Para hacer posible el casting necesitamos un padre común para ambos
List<A>
yList<B>
:List<?>
por ejemplo. Lo siguiente es válido:Sin embargo, recibirá una advertencia. Puede suprimirlo agregando
@SuppressWarnings("unchecked")
a su método.fuente
Con Java 8, en realidad puedes
fuente
new List<TestB>
, un bucle y un elenco?Sin embargo, creo que está lanzando en la dirección incorrecta ... si el método devuelve una lista de
TestA
objetos, entonces no es seguro enviarlosTestB
.Básicamente, le está pidiendo al compilador que le permita realizar
TestB
operaciones en un tipoTestA
que no los admite.fuente
Dado que esta es una pregunta ampliamente referenciada, y las respuestas actuales explican principalmente por qué no funciona (o proponen soluciones extravagantes y peligrosas que nunca me gustaría ver en el código de producción), creo que es apropiado agregar otra respuesta, mostrando las trampas y una posible solución.
La razón por la cual esto no funciona en general ya se ha señalado en otras respuestas: si la conversión es realmente válida o no depende de los tipos de objetos que están contenidos en la lista original. Cuando hay objetos en la lista cuyo tipo no es de tipo
TestB
, sino de una subclase diferente deTestA
, entonces la conversión no es válida.Por supuesto, los moldes pueden ser válidos. A veces tiene información sobre los tipos que no está disponible para el compilador. En estos casos, es posible emitir las listas, pero en general, no se recomienda :
Uno podría ...
Las implicaciones del primer enfoque (que corresponde a la respuesta actualmente aceptada) son sutiles. Puede parecer que funciona correctamente a primera vista. Pero si hay tipos incorrectos en la lista de entrada, se
ClassCastException
arrojará un , tal vez en una ubicación completamente diferente en el código, y puede ser difícil depurar esto y descubrir dónde se deslizó el elemento incorrecto en la lista. El peor problema es que alguien podría incluso agregar los elementos inválidos después de que la lista ha sido lanzada, lo que dificulta aún más la depuración.El problema de depurar estos espurios
ClassCastExceptions
puede aliviarse con laCollections#checkedCollection
familia de métodos.Filtrar la lista según el tipo
Una forma más segura de convertir de una
List<Supertype>
a unaList<Subtype>
es filtrar la lista y crear una nueva lista que contenga solo elementos que tengan cierto tipo. Hay algunos grados de libertad para la implementación de dicho método (por ejemplo, con respecto al tratamiento de lasnull
entradas), pero una posible implementación puede tener este aspecto:Este método se puede utilizar para filtrar listas arbitrarias (no solo con una relación Subtipo-Supertipo dada con respecto a los parámetros de tipo), como en este ejemplo:
fuente
No se puede echar
List<TestB>
aList<TestA>
como lo menciona Steve Kuo pero usted puede volcar el contenido deList<TestA>
dentroList<TestB>
. Intenta lo siguiente:No he probado este código, por lo que probablemente haya errores, pero la idea es que debería iterar a través del objeto de datos agregando los elementos (objetos TestB) a la Lista. Espero que eso funcione para usted.
fuente
La mejor manera segura es implementar
AbstractList
y lanzar elementos en la implementación. He creadoListUtil
clase de ayuda:Puede usar el
cast
método para lanzar objetos a ciegas en la lista y elconvert
método para el lanzamiento seguro. Ejemplo:fuente
La única forma que sé es copiando:
fuente
Cuando emite una referencia de objeto, solo está emitiendo el tipo de referencia, no el tipo de objeto. el lanzamiento no cambiará el tipo real del objeto.
Java no tiene reglas implícitas para convertir tipos de objetos. (A diferencia de las primitivas)
En su lugar, debe proporcionar cómo convertir un tipo a otro y llamarlo manualmente.
Esto es más detallado que en un lenguaje con soporte directo, pero funciona y no debería necesitar hacer esto con mucha frecuencia.
fuente
Si tiene un objeto de la clase
TestA
, no puede lanzarloTestB
. todoTestB
es unTestA
, pero no al revés.en el siguiente código:
la segunda línea arrojaría un
ClassCastException
.solo puede emitir una
TestA
referencia si el objeto en sí lo esTestB
. por ejemplo:por lo tanto, no siempre puede emitir una lista
TestA
a una lista deTestB
.fuente
Puede usar el
selectInstances
método en Eclipse Collections . Sin embargo, esto implicará crear una nueva colección, por lo que no será tan eficiente como la solución aceptada que utiliza la conversión.Incluí
StringBuffer
en el ejemplo para mostrar queselectInstances
no solo rechaza el tipo, sino que también filtra si la colección contiene tipos mixtos.Nota: Soy un committer para Eclipse Collections.
fuente
Esto es posible debido al tipo de borrado. Encontrarás que
Internamente ambas listas son de tipo
List<Object>
. Por esa razón no puedes lanzar uno al otro, no hay nada que lanzar.fuente
Integer j = 42; Integer i = (Integer) j;
funciona bien, ambos son enteros, ambos tienen la misma clase, por lo que "no hay nada que emitir".El problema es que su método NO devuelve una lista de TestA si contiene un TestB, entonces, ¿qué sucede si se escribió correctamente? Entonces este elenco:
funciona tan bien como podrías esperar (Eclipse te advierte de un elenco sin control que es exactamente lo que estás haciendo, así que meh). Entonces, ¿puedes usar esto para resolver tu problema? En realidad puedes debido a esto:
Exactamente lo que pediste, ¿verdad? o para ser realmente exacto:
Parece compilar bien para mí.
fuente
Esta prueba muestra cómo lanzar
List<Object>
aList<MyClass>
. Pero debe prestar atención a queobjectList
debe contener instancias del mismo tipo queMyClass
. Y este ejemplo puede considerarse cuandoList<T>
se usa. Para este propósito, obtenga el campoClass<T> clazz
en el constructor y úselo en lugar deMyClass.class
.fuente
Esto debería funcionar
fuente
Es bastante extraño que la transmisión manual de una lista todavía no sea proporcionada por alguna caja de herramientas que implemente algo como:
Por supuesto, esto no verificará los elementos uno por uno, pero eso es precisamente lo que queremos evitar aquí, si sabemos bien que nuestra implementación solo proporciona el subtipo.
fuente