Autoboxing de Java y locura del operador ternario.
operator-keyword ternary (4)
Bueno, Integer.valueOf(String)
devuelve un Integer
y -1 es un int
primitivo. El primer ejemplo es forzado a unbox porque un término es primitivo. También podrías haber usado
Integer boxedRsrq = boxedPci != null ?
rsrqs.get(boxedPci.toString()) : (Integer) -1;
Lo que habría encajonado el -1.
Acabo de pasar un par de horas frustrantes depurando este código:
LinkedHashMap<String, Integer> rsrqs = new LinkedHashMap<String, Integer>();
Integer boxedPci = 52;
Integer boxedRsrq = boxedPci != null ? rsrqs.get(boxedPci.toString()) : -1;
Lo anterior produce una NullPointerException. El siguiente código no lo hace:
LinkedHashMap<String, Integer> rsrqs = new LinkedHashMap<String, Integer>();
Integer boxedPci = 52;
Integer boxedRsrq = boxedPci != null ? rsrqs.get(boxedPci.toString()) : Integer.valueOf(-1);
La única diferencia es envolver el -1 con Integer.valueOf (). Estoy seguro de que voy a golpearme la frente una vez que alguien explique por qué este código se comporta de la manera en que lo hace ... pero, ¿puede alguien explicarme por qué este código se comporta de la manera que lo hace :)?
- editar
Pensándolo bien, sospecho que el NPE viene de rsrqs.get () devolviendo nulo, lo que creo que Java está intentando desempaquetar en un int, antes de volver a un entero. Integer.valueOf () obliga a Java a realizar el paso de unbox-box. Moraleja de la historia; no ignore esas advertencias de boxeo en Eclipse;)
La explicación puede concluirse a partir de la información en la especificación del lenguaje java: 15.25. ¿Operador condicional? : .
De la tabla de allí, obtiene la información de que, si el segundo operando ( rsrqs.get(boxedPci.toString())
) es de tipo Integer
y el tercer operando es de tipo int
, el resultado será de tipo int
.
Eso sin embargo significa que
Integer boxedRsrq = boxedPci != null ? rsrqs.get(boxedPci.toString()) : -1;
es semánticamente lo mismo que
Integer boxedRsrq = boxedPci != null ? ((int)rsrqs.get(boxedPci.toString())) : -1;
Pero eso significa que obtienes una NullPointerException
, si obtienes un null
del mapa, lo que obviamente sucede.
Si convierte el tercer operando en Integer
, el segundo operando nunca se convertirá en int
y no ocurre NPE.
Las expresiones ternarias, como cualquier expresión, tienen un tipo determinado por el compilador. Si los dos lados de la expresión ternaria tienen lo que parecen tipos diferentes, entonces el compilador intentará encontrar un tipo base común utilizando la menos ambigua de las dos opciones. En su caso, el -1
es el menos ambiguo, por lo que el tipo de expresión ternaria es int
. Lamentablemente, el compilador no usa la inferencia de tipos basada en la variable de recepción.
La expresión rsrqs.get(boxedPci.toString())
luego se evalúa y se fuerza en el tipo int
para que coincida con la expresión ternaria, pero como es null
, arroja el NPE.
Al encajonar el -1
, el valor de la expresión ternaria es Integer
, por lo que está a salvo de nulos.
1
es un int, no un entero. Entonces, Java va a desmarcar su Integer a int, lo que causa la excepción NullPointerException. Cuando auto-unbox un entero nulo, se traduce en una NullPointerException. ( referencia aquí )
Pero cuando usas
Integer.valueOf(-1)
no es necesario que lo desmarque automáticamente, lo que no conlleva excepciones.