Comportamento ternario dispari di Java durante l'assegnazione di valore. Cosa sta facendo Java dietro le quinte affinché ciò accada?


10

Qualche giorno fa, mi sono imbattuto in uno scenario affascinante che non riuscivo a trovare alcuna documentazione su come o perché Java permettesse quanto segue. (Questo frammento è solo una forma semplificata del 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);
   }

nello snippet sopra:

se bool = true, otterrai il valore '5';

ma se bool = false, si ottiene un'eccezione puntatore null quando si tenta di valutare l'operazione ternaria. NON la dichiarazione di stampa.


Per risolvere questo problema, cambio semplicemente 'risultato' in

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

In questo modo, fornirò il comportamento previsto di cui avevo bisogno:

se bool = true, otterrai il valore '5';

ma se bool = false, allora ottieni 'null'


ora la parte divertente è che se lo dividi in una normale istruzione if / else, allora java NON ti consente di compilare

longVal = intVal; 

ma non lo rileva tramite l'operatore ternario. Quindi cosa sta facendo Java per renderlo nullo nello snippet originale?

(java 11)

Risposte:


10

Quando lo fai:

Long result = bool ? intVal : longVal

Questa espressione sta restituendo un longe, quando boolè falso, tenta di decomprimere nullun valore Long per adattarsi alla resultvariabile e genera un NPE.

Quando lo fai:

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

Questa espressione sta già tornando, Longquindi non è necessario unboxing e il nullvalore è assegnato correttamente alla resultvariabile.

Riferimento:

Come discusso nella sezione commenti, per comprendere meglio perché ciò accada, controllare le seguenti sezioni di JLS:


Sono sorpreso dalla tua diversa tabella di riferimento da 15.25 A a E dove è "chiaro" che Integer / Long si traduca in un bnp (Integer, Long).
matt

Buona risposta. Generalmente quando non ne ho assolutamente idea, cosa succede dentro, consiglio di dare un'occhiata al bytecode compilato, che rivela quasi esattamente ciò che ha descritto. Almeno quando si estrae uno snippet di codice minimo, è più o meno una questione di formazione, come leggerlo e comprenderlo.
Jan tenuto il

Come afferma JLS, la Sezione 5.6.2 : "Se un operando è di tipo di riferimento, è soggetto a conversione unboxing"; quindi viene applicato "Ampliamento della conversione primitiva (§5.1.2) [..]"
Diego Magdaleno,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.