Por que isso joga 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;
}
enquanto isso não
public static void main(String[] args) throws Exception {
Boolean b = true ? null : false;
System.out.println(b); // null
}
?
A solução é pela maneira a substituir false
por Boolean.FALSE
evitar null
sendo desemoldurado para boolean
--que não é possível. Mas essa não é a questão. A questão é por quê ? Há alguma referência no JLS que confirme esse comportamento, especialmente no 2º caso?
Respostas:
A diferença é que o tipo explícito do
returnsNull()
método afeta a digitação estática das expressões no tempo de compilação:Consulte Especificação da linguagem Java, seção 15.25 Operador condicional? :
Para E1, os tipos dos segundo e terceiro operandos são
Boolean
eboolean
respectivamente, portanto, esta cláusula se aplica:Como o tipo da expressão é
boolean
, o segundo operando deve ser coagido paraboolean
. O compilador insere o código de descompactação automática no 2º operando (valor de retorno dereturnsNull()
) para fazê-lo digitarboolean
. Obviamente, isso faz com que o NPEnull
retorne no tempo de execução.Para E2, os tipos do 2º e 3º operandos são
<special null type>
(nãoBoolean
como em E1!) Eboolean
respectivamente, portanto, nenhuma cláusula de digitação específica se aplica ( leia-os! ); Portanto, a cláusula final "caso contrário" se aplica:<special null type>
(ver §4.1 )boolean
<special null type>
(veja o último item na lista de conversões de boxe em §5.1.7 )Boolean
Portanto, o tipo da expressão condicional é
Boolean
e o terceiro operando deve ser coagidoBoolean
. O compilador insere o código de boxe automático para o terceiro operando (false
). O segundo operando não precisa do desempacotamento automático como emE1
, portanto, nenhum NPE de desempacotamento automáticonull
é retornado.Esta pergunta precisa de uma análise de tipo semelhante:
Operador condicional Java?: Tipo de resultado
fonte
lub
delub(T1,T2)
suporte para?A linha:
é transformado internamente para:
executar o unboxing; assim:
null.booleanValue()
produzirá um NPEEssa é uma das principais armadilhas ao usar o autoboxing. Esse comportamento é realmente documentado no 5.1.8 JLS
Edit: Eu acredito que o unboxing é devido ao terceiro operador ser do tipo booleano, como (elenco implícito adicionado):
fonte
Na especificação da linguagem Java, seção 15.25 :
Portanto, o primeiro exemplo tenta chamar
Boolean.booleanValue()
para converterBoolean
paraboolean
conforme a primeira regra.No segundo caso, o primeiro operando é do tipo nulo, quando o segundo não é do tipo de referência, portanto, a conversão de caixa automática é aplicada:
fonte
null
.boolean
não é um tipo de referência.Podemos ver esse problema no código de bytes. Na linha 3 do código de bytes principal
3: invokevirtual #3 // Method java/lang/Boolean.booleanValue:()Z
, o booleano de box de valor nulo,invokevirtual
o métodojava.lang.Boolean.booleanValue
, lançará o NPE, é claro.fonte