java - metodo - Booleanos, operadores condicionales y autoboxing
operadores logicos en java ejemplos (4)
De Java Language Specification, sección 15.25 :
- Si uno de los operandos segundo y tercero es de tipo booleano y el tipo de otro es de tipo booleano, entonces el tipo de expresión condicional es booleano.
Entonces, el primer ejemplo intenta llamar a Boolean.booleanValue()
para convertir Boolean
a boolean
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 autoboxeo:
- 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).
¿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 que null
sea unboxed a 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?
La línea:
Boolean b = true ? returnsNull() : false;
se transforma internamente a:
Boolean b = true ? returnsNull().getBoolean() : false;
para realizar el desempaquetado; así: null.getBoolean()
producirá un NPE
Este es uno de los principales escollos al usar el autoboxing. Este comportamiento está de hecho documentado en 5.1.8 JLS
Editar: creo que el desempaquetado se debe a que el tercer operador es de tipo booleano, como (agregado implícito):
Boolean b = (Boolean) true ? true : false;
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étodo java.lang.Boolean.booleanValue
, arrojará NPE por supuesto.
public static void main(java.lang.String[]) throws java.lang.Exception;
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: invokestatic #2 // Method returnsNull:()Ljava/lang/Boolean;
3: invokevirtual #3 // Method java/lang/Boolean.booleanValue:()Z
6: invokestatic #4 // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
9: astore_1
10: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
13: aload_1
14: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
17: return
LineNumberTable:
line 3: 0
line 4: 10
line 5: 17
Exceptions:
throws java.lang.Exception
public static java.lang.Boolean returnsNull();
descriptor: ()Ljava/lang/Boolean;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: aconst_null
1: areturn
LineNumberTable:
line 8: 0
La diferencia es que el tipo explícito del método returnsNull()
afecta al tipado estático de las expresiones en tiempo de compilación:
E1: `true ? returnsNull() : false` - boolean (auto-unboxing 2nd operand to boolean)
E2: `true ? null : false` - Boolean (autoboxing of 3rd operand to Boolean)
Consulte Especificación del lenguaje Java, sección 15.25 Operador condicional? :
Para E1, los tipos de los operandos 2º y 3º son
Boolean
yboolean
respectivamente, por lo que esta cláusula se aplica:Si uno de los operandos segundo y tercero es de tipo booleano y el tipo de otro es de tipo booleano, entonces el tipo de expresión condicional es booleano.
Como el tipo de expresión es
boolean
, el 2º operando debe ser forzado aboolean
. El compilador inserta el código de auto-unboxing en el 2do operando (valor de retorno dereturnsNull()
) para hacer que escribaboolean
. Esto, por supuesto, hace que el NPE delnull
devuelto en el tiempo de ejecución.Para E2, los tipos de los operandos 2 y 3 son
<special null type>
(¡noBoolean
como en E1!) Yboolean
respectivamente, por lo que no se aplica una cláusula de tipaje específica ( ¡léalo! ), Por lo que se aplica la cláusula final "de otro modo" :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).
- S1 ==
<special null type>
(ver §4.1 ) - S2 ==
boolean
- T1 == box (S1) ==
<special null type>
(ver último elemento en la lista de conversiones de boxeo en §5.1.7 ) - T2 == box (S2) == `Booleano
- lub (T1, T2) ==
Boolean
Entonces, el tipo de expresión condicional es
Boolean
y el 3er operando debe ser forzado aBoolean
. El compilador inserta el código de auto-boxing para el 3er operando (false
). El segundo operando no necesita el auto-unboxing como enE1
, por lo que no se desempaqueta automáticamente NPE cuando se devuelvenull
.- S1 ==
Esta pregunta necesita un análisis de tipo similar: