Dziwne zachowanie trójskładnikowe Java podczas przypisywania wartości. Co robi Java za kulisami, aby tak się stało?

10

Kilka dni temu natknąłem się na fascynujący scenariusz, w którym nie mogłem znaleźć żadnej dokumentacji dotyczącej tego, jak i dlaczego Java pozwala na następujące zdarzenia. (Ten fragment kodu jest tylko uproszczoną formą błędu).

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

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

we fragmencie powyżej:

jeśli bool = true, to otrzymujesz wartość „5”;

ale jeśli bool = false, wówczas podczas próby oceny operacji trójkowej pojawia się wyjątek wskaźnika zerowego. NIE instrukcja drukowania.


Aby to naprawić, po prostu zmieniam „wynik” na

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

Dzięki temu uzyskasz oczekiwane zachowanie, którego potrzebowałem:

jeśli bool = true, to otrzymujesz wartość „5”;

ale jeśli bool = false, to dostajesz „null”


teraz zabawne jest to, że jeśli podzielisz to na normalną instrukcję if / else, wówczas Java nie pozwala na kompilację

longVal = intVal; 

ale nie łapie tego za pośrednictwem operatora trójskładnikowego. Co więc robi Java, aby w oryginalnym fragmencie była zerowa?

(java 11)

Tim Z.
źródło

Odpowiedzi:

10

Kiedy to zrobisz:

Long result = bool ? intVal : longVal

To wyrażenie zwraca a, longa gdy booljest fałszem, próbuje rozpakować nulldo wartości Long w celu dopasowania do resultzmiennej i generuje NPE.

Kiedy to zrobisz:

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

To wyrażenie już zwraca, Longwtedy nie ma potrzeby rozpakowywania, a nullwartość jest pomyślnie przypisywana do resultzmiennej.

Odniesienie:

Jak omówiono w sekcji komentarzy, aby lepiej zrozumieć, dlaczego tak się dzieje, sprawdź następujące sekcje JLS:

Diego Magdaleno
źródło
Dziwię się, że masz inną tabelę odniesienia 15.25 A do E, w której „jasne” jest, że liczba całkowita / długa daje wynik bnp (liczba całkowita, długa).
mat
Dobra odpowiedź. Generalnie, gdy absolutnie nie mam pojęcia, co się dzieje wewnątrz, polecam zajrzeć do skompilowanego kodu bajtowego, który pokazuje prawie dokładnie to, co opisano. Przynajmniej podczas rozpakowywania minimalnego fragmentu kodu jest to mniej więcej kwestia szkolenia, jak go czytać i rozumieć.
Jan Held
Jak mówi JLS, Rozdział 5.6.2 : „Jeżeli dowolny argument jest typu referencyjnego, podlega konwersji rozpakowywania”; następnie stosuje się „rozszerzenie pierwotnej konwersji (§5.1.2) [..]”
Diego Magdaleno