Comportamento ternário estranho do Java ao atribuir valor. O que o Java está fazendo nos bastidores para que isso aconteça?

10

Alguns dias atrás, encontrei um cenário fascinante em que não encontrava nenhuma documentação sobre como ou por que o Java permite que o seguinte aconteça. (Este trecho é apenas uma forma simplificada do bug.)

    @Test
    public void test() {
      boolean bool = false;
      Integer intVal = Integer.valueOf(5);
      Long longVal = null;
      Long result = bool ? intVal : longVal;

      System.out.println(" > " + result);
   }

no trecho acima:

se o bool = true, então você obtém o valor '5';

mas se bool = false, você recebe uma exceção de ponteiro nulo ao tentar avaliar a operação ternária. NÃO a declaração de impressão.


Para consertar isso, mudei 'resultado' para

Long result = bool ? Long.valueOf(intVal) : longVal;

Fazer isso dará o comportamento esperado que eu precisava:

se o bool = true, então você obtém o valor '5';

mas se bool = false, você obtém 'null'


agora a parte divertida é que, se você dividir isso em uma instrução if / else normal, o java NÃO permitirá que você compile

longVal = intVal; 

mas isso não ocorre através do operador ternário. Então, o que o Java está fazendo para torná-lo nulo no snippet original?

(java 11)

Tim Z.
fonte

Respostas:

10

Quando você faz isso:

Long result = bool ? intVal : longVal

Essa expressão está retornando ae long, quando boolfalse, tenta unboxe nullpara um valor Long para ajustar a resultvariável e lança um NPE.

Quando você faz isso:

Long result = bool ? Long.valueOf(intVal) : longVal

Esta expressão já está retornando, Longentão não há necessidade de remover a caixa de seleção e o nullvalor é atribuído com êxito à resultvariável.

Referência:

Conforme discutido na seção de comentários, para entender melhor por que isso acontece, verifique as seguintes seções do JLS:

Diego Magdaleno
fonte
Estou surpreso com a tabela de referência diferente 15.25 A a E, onde é "claro" que Inteiro / Longo resulta em um bnp (Inteiro, Longo).
matt
Boa resposta. Geralmente, quando não tenho absolutamente nenhuma idéia do que está acontecendo lá dentro, recomendo dar uma olhada no código de código compilado, que revela quase exatamente o que foi descrito. Pelo menos ao extrair um snippet de código mínimo, é mais ou menos uma questão de treinamento como ler e entender.
Jan Held
Como o JLS, a Seção 5.6.2 diz: "Se algum operando é de um tipo de referência, está sujeito à conversão de unboxing"; então "A conversão primitiva de alargamento (§5.1.2) é aplicada [..]"
Diego Magdaleno