valido usar ternario ternaria operador mejorado ejemplos busqueda anidado java generics ternary-operator bounded-wildcard

java - usar - ¿Por qué el operador ternario no le gusta los tipos genéricos con comodines delimitados?



operador ternario ts (2)

La siguiente clase define dos métodos, ambos intuitivamente tienen la misma funcionalidad. Cada función se llama con dos listas de tipo List<? super Integer> List<? super Integer> y un valor booleano que especifica cuál de esas listas debe asignarse a una variable local.

import java.util.List; class Example { void chooseList1(boolean choice, List<? super Integer> list1, List<? super Integer> list2) { List<? super Integer> list; if (choice) list = list1; else list = list2; } void chooseList2(boolean choice, List<? super Integer> list1, List<? super Integer> list2) { List<? super Integer> list = choice ? list1 : list2; } }

De acuerdo con javac 1.7.0_45 , javac 1.7.0_45 es válido mientras chooseList2 no. Se queja:

java: incompatible types required: java.util.List<? super java.lang.Integer> found: java.util.List<capture#1 of ? extends java.lang.Object>

Sé que las reglas para encontrar el tipo de expresión que contiene el operador ternario ( … ? … : … ) son bastante complejas, pero, por lo que yo las entiendo, elige el tipo más específico para el que tanto el segundo como el tercer argumento pueden ser convertido sin un lanzamiento explícito. Aquí, esto debería ser List<? super Integer> list1 List<? super Integer> list1 pero no lo es.

Me gustaría ver una explicación de por qué este no es el caso, de preferencia con una referencia de la Especificación del lenguaje Java y una explicación intuitiva de lo que podría salir mal si no se previene.


El tiempo pasa y Java cambia. Me complace informarles que desde Java 8, probablemente debido a la introducción de "mecanografía de destino" , el ejemplo de Feuermurmels se compila sin problemas.

La versión actual de la sección relevante de JLS dice:

Como las expresiones condicionales de referencia pueden ser expresiones poli, pueden "pasar" contexto a sus operandos.

...

También permite el uso de información adicional para mejorar la verificación de tipos de invocaciones de métodos genéricos. Antes de Java SE 8, esta tarea estaba bien escrita:

List<String> ls = Arrays.asList();

pero esto no fue:

List<String> ls = ... ? Arrays.asList() : Arrays.asList("a","b");

Las reglas anteriores permiten que ambas asignaciones se consideren bien tipadas.

También es interesante observar que el siguiente, derivado del código de Sotirios Delimanolis no compila:

void tryIt(List<? super Integer> list1, List<? super Integer> list2) { List<? super Integer> l1 = list1 == list2 ? list1 : list2; // Works fine List<? super Integer> l2 = test(list1, list2); // Error: Type mismatch }

Esto sugiere que la información disponible cuando se calcula el tipo de límite inferior en el tipo de test de retorno es diferente de la del tipo de operador condicional. Por qué este es el caso, no tengo ni idea, podría ser una pregunta interesante en sí misma.

Yo uso jdk_1.8.0_25.


Esta respuesta se aplica a Java 7.

La Especificación del lenguaje Java establece lo siguiente sobre el operador condicional ( ? :

De lo contrario, el segundo y tercer operandos son de tipos S1 y S2, respectivamente. Deje que T1 sea del tipo que resulta de aplicar la conversión de boxeo a S1, y deje que T2 sea del tipo que resulta de aplicar la conversión de boxeo a S2.

El tipo de expresión condicional es el resultado de aplicar la conversión de captura (§5.1.10) a lub (T1, T2) (§15.12.2.7).

En la expresión

List<? super Integer> list = choice ? list1 : list2;

T1 es List<capture#1? super Integer> List<capture#1? super Integer> y T2 es List<capture#2? super Integer> List<capture#2? super Integer> . Ambos tienen límites inferiores.

Este artículo entra en detalles sobre cómo calcular lub(T1, T2) (o join function ). Tomemos un ejemplo de allí

<T> T pick(T a, T b) { return null; } <C, A extends C, B extends C> C test(A a, B b) { return pick(a, b); // inferred type: Object } void tryIt(List<? super Integer> list1, List<? super Integer> list2) { test(list1, list2); }

Si usa un IDE y pasa el mouse sobre la test(list1, list2) , notará que el tipo de retorno es

List<? extends Object>

Esto es lo mejor que puede hacer la inferencia de tipos de Java. si list1 era List<Object> y list2 era List<Number> , el único tipo de devolución aceptable es List<? extends Object> List<? extends Object> . Como este caso debe cubrirse, el método siempre debe devolver ese tipo.

Del mismo modo en

List<? super Integer> list = choice ? list1 : list2;

El lub(T1, T2) es nuevamente List<? extends Object> List<? extends Object> y su conversión de captura es List<capture#XX of ? extends Object> List<capture#XX of ? extends Object> .

Finalmente, ¿una referencia de tipo List<capture#XX of ? extends Object> List<capture#XX of ? extends Object> no se puede asignar a una variable de tipo List<? super Integer> List<? super Integer> y entonces el compilador no lo permite.