No se puede convertir de la Lista a la Lista <Lista >

Una lista sin formato se convierte en List Simplemente bien. ¿Por qué una lista de listas sin procesar no puede convertirse a una lista de la List ?

 { // works List raw = null; List wild = raw; } { // Type mismatch: cannot convert from List to List<List> List raw = null; List<List> wild = raw; } 

Trasfondo (para mitigar el problema xy ):

Una API que estoy usando devuelve List . Sé que siempre es List<JAXBElement> . Planeo hacer un bucle y crear mi propia List , pero estaba tratando de corregir (pero no suprimir) la advertencia del comstackdor de tipo sin List raw = api(); cuando escribo List raw = api(); .

Lo intenté:

 List<JAXBElement> raw = api(); List<JAXBElement> raw = (List<JAXBElement>) api(); 

pero estos dan el error de desajuste de tipo.

Curiosamente, esto no da advertencia o error:

 for (JAXBElement e : api()) { // ... } 

 // #1 (does compile) List raw = null; List wild = raw; // #2 (doesn't compile) List raw = null; List> wild = raw; 

Primero, vamos a aclarar por qué estas son asignaciones realmente no relacionadas. Es decir, están gobernados por reglas diferentes.

# 1 se llama conversión sin marcar :

Existe una conversión no verificada de la clase o tipo de interfaz sin procesar ( §4.8 ) G a cualquier tipo parametrizado de la forma G1 ,...,T n > .

Específicamente, es un caso especial de contexto de asignación solo para este escenario:

Si, después de haber aplicado [otras posibles conversiones], el tipo resultante es un tipo sin formato, entonces se puede aplicar una conversión no verificada.

# 2 requiere una conversión de tipo de referencia; sin embargo, el problema es que no se trata de una conversión de ampliación (que es el tipo de conversión de referencia que se permite implícitamente sin conversión).

¿Porqué es eso? Bueno, esto se rige específicamente por las reglas de subtipado genérico y más específicamente este punto:

Dada una statement de tipo genérico C1 ,...,F n > ( n > 0), los supertipos directos del tipo parametrizado C1 ,...,T n > , donde T i (1 ≤ i)n ) es un tipo, son todos los siguientes:

  • C1 ,...,S n > , donde S i contiene T i (1 ≤ in ).

Esto nos remite a algo que JLS llama contención , donde para ser una asignación válida, los argumentos del lado izquierdo deben contener los argumentos del lado derecho. La contención rige en gran medida la subtipificación genérica ya que los tipos generics “concretos” son invariables .

Puede estar familiarizado con las ideas que:

  • una List no es una List
  • pero una List es una List List .

Bueno, esto último es cierto porque ? extends Animal ? extends Animal contiene Dog .

Entonces, la pregunta se convierte en “¿contiene un argumento de tipo List Un argumento de tipo sin formato List ? Y la respuesta es no: aunque List Es un subtipo de List , esta relación no se cumple para los argumentos de tipo.

No existe una regla especial que lo haga cierto: List> no es un subtipo de List por esencialmente el mismo motivo. List no es un subtipo de List .

Entonces, como List no es un subtipo de List> , la asignación no es válida. Del mismo modo, no puede realizar un molde de conversión de estrechamiento directo porque List tampoco es un supertipo de List> .


Para realizar la tarea, aún puedes aplicar un reparto. Hay tres formas de hacerlo que me parecen razonables.

 // 1. raw type @SuppressWarnings("unchecked") List> list0 = (List) api(); // 2. slightly safer @SuppressWarnings({"unchecked", "rawtypes"}) List> list1 = (List>) (List) api(); // 3. avoids a raw type warning @SuppressWarnings("unchecked") List> list2 = (List>) (List>) api(); 

(Puede sustituir JAXBElement por la List interna).

Su caso de uso para este casting debería ser seguro porque List> es un tipo más restrictivo que List .

  • La instrucción de tipo sin procesar es una asignación ampliada y luego una asignación sin marcar. Esto funciona porque, como se muestra arriba, cualquier tipo parametrizado se puede convertir a su tipo sin procesar y viceversa.

  • La statement un poco más segura (nombrada así porque pierde menos información de tipo) es un reparto ensanchado que luego reduce el lanzamiento. Esto funciona mediante conversión a un supertipo común:

      List ╱ ╲ List> List 

    El comodín delimitado permite que los argumentos de tipo sean considerados para la subtipificación a través de la contención.

    ¿El hecho de que List List se considera un supertipo de List> se puede probar con transitivity:

    1. ? extends List ? extends List contiene ? extends List ? extends List , porque List es un supertipo de List .

    2. ? extends List ? extends List contiene List .

    3. Por lo tanto ? extends List ? extends List contiene List .

    (Es decir, List :> List> :> List> )

  • El tercer ejemplo funciona de una manera similar al segundo ejemplo, mediante conversión a un supertipo común List> List> . Como no usa un tipo sin formato, podemos suprimir una advertencia menos.


El resumen no técnico aquí es que la especificación implica que no hay subtipo ni relación de supertipo entre List y List> .

Aunque la conversión de List a List> debería ser segura , no está permitida. (Es seguro porque ambos son una List que puede almacenar cualquier tipo de List , pero una List> impone más restricciones sobre cómo se pueden usar sus elementos después de que se recuperan).

Desafortunadamente, no hay una razón práctica que no compile, excepto que los tipos crudos son extraños y el uso de ellos es problemático.

No puede asignarlo ni emitirlo directamente , porque Raw List no es lo mismo que List .

Al usar la verificación de tipo de List se ignora y puede utilizar cualquier método genérico con cualquier tipo. Al usar List El comstackdor no le permitirá usar métodos con parámetros generics .


Por lo tanto, puedes ignorar las advertencias:

 @SuppressWarnings("rawtypes") 

Y / o lanzarlo explícitamente con una solución alternativa:

 List> raw = (List>) ((Object)api()); 

Si solo desea eliminar advertencias, puede usar @SuppressWarnings (“tipos de raw”).

Básicamente, el problema es que el comstackdor trata al tipo en bruto como un objeto primitivo generics previos, por lo que … un “objeto antiguo” no es un “objeto genérico”, por lo que … no puede convertirlos.

Lea esto del documento oficial: http://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html

Pero si asigna un tipo sin procesar a un tipo parametrizado, recibe una advertencia:

Caja rawBox = new Box (); // rawBox es un tipo sin formato de Box Box intBox = rawBox; // advertencia: conversión no comprobada También recibe una advertencia si usa un tipo sin procesar para invocar métodos generics definidos en el tipo genérico correspondiente:

Box stringBox = new Box <> (); Caja rawBox = stringBox; rawBox.set (8); // advertencia: invocación sin marcar para establecer (T) La advertencia muestra que los tipos sin procesar omiten las comprobaciones de tipo genérico, lo que retrasa la captura del código inseguro en el tiempo de ejecución. Por lo tanto, debe evitar el uso de tipos sin procesar.