¿Por qué esto arroja NullPointerException
public static void main(String[] args) throws Exception {
Boolean b = true ? returnsNull() : false; // NPE on this line.
System.out.println(b);
}
public static Boolean returnsNull() {
return null;
}
mientras esto no
public static void main(String[] args) throws Exception {
Boolean b = true ? null : false;
System.out.println(b); // null
}
?
La solución es, por cierto, reemplazar false
por Boolean.FALSE
para evitar null
ser desempaquetado en, boolean
lo que no es posible. Pero esa no es la pregunta. La pregunta es ¿por qué ? ¿Hay alguna referencia en JLS que confirme este comportamiento, especialmente del segundo caso?
Respuestas:
La diferencia es que el tipo explícito del
returnsNull()
método afecta el tipeo estático de las expresiones en tiempo de compilación:Consulte Especificación del lenguaje Java, sección 15.25 ¿Operador condicional? :
Para E1, los tipos de los operandos segundo y tercero son
Boolean
yboolean
respectivamente, por lo que se aplica esta cláusula:Como el tipo de expresión es
boolean
, el segundo operando debe ser forzadoboolean
. El compilador inserta el código de desempaquetado automático en el segundo operando (valor de retorno dereturnsNull()
) para que escribaboolean
. Por supuesto, esto hace que el NPE senull
devuelva en tiempo de ejecución.Para E2, los tipos de los operandos segundo y tercero son
<special null type>
(¡noBoolean
como en E1!) Yboolean
respectivamente, por lo que no se aplica una cláusula de escritura específica (¡ vaya a leerlos! ), Por lo que se aplica la cláusula final "de lo contrario":<special null type>
(ver §4.1 )boolean
<special null type>
(ver el último elemento en la lista de conversiones de boxeo en §5.1.7 )Boolean
Entonces, el tipo de expresión condicional es
Boolean
y el tercer operando debe ser forzadoBoolean
. El compilador inserta el código de auto-boxeo para el tercer operando (false
). El segundo operando no necesita el desempaquetado automático como enE1
, por lo que no se requiere un NPE de desempaquetado automático cuandonull
se devuelve.Esta pregunta necesita un análisis de tipo similar:
Operador condicional de Java?: Tipo de resultado
fuente
lub
enlub(T1,T2)
?La línea:
se transforma internamente en:
para realizar el unboxing; así:
null.booleanValue()
producirá un NPEEsta es una de las principales dificultades al usar el autoboxing. Este comportamiento está documentado en 5.1.8 JLS
Editar: creo que el unboxing se debe a que el tercer operador es de tipo booleano, como (agregado implícito):
fuente
De Java Language Specification, sección 15.25 :
Entonces, el primer ejemplo intenta llamar
Boolean.booleanValue()
para convertirBoolean
aboolean
según la primera regla.En el segundo caso, el primer operando es del tipo nulo, cuando el segundo no es del tipo de referencia, por lo que se aplica la conversión de autoboxing:
fuente
null
.boolean
que no es un tipo de referencia.Podemos ver este problema desde el código de bytes. En la línea 3 del código de byte principal
3: invokevirtual #3 // Method java/lang/Boolean.booleanValue:()Z
, el boxeo Booleano de valor nulo,invokevirtual
el métodojava.lang.Boolean.booleanValue
, arrojará NPE, por supuesto.fuente