Pierwszą rzeczą, o której należy pamiętać, jest to, że trójskładnikowe operatory Java mają „typ”, i to właśnie kompilator określi i rozważy bez względu na rzeczywiste / rzeczywiste typy drugiego lub trzeciego parametru. W zależności od kilku czynników typ operatora trójskładnikowego określa się na różne sposoby, jak pokazano w specyfikacji języka Java 15.26
W powyższym pytaniu powinniśmy rozważyć ostatni przypadek:
W przeciwnym razie drugi i trzeci argument są odpowiednio typu S1 i S2 . Niech T1 będzie typem wynikającym z zastosowania konwersji boksu do S1 , i niech T2 będzie typem wynikającym z zastosowania konwersji boksu do S2 . Rodzaj wyrażenia warunkowego wynika z zastosowania konwersji przechwytywania (§5.1.10) na lub (T1, T2) (§15.12.2.7).
Jest to zdecydowanie najbardziej złożony przypadek, gdy przyjrzysz się zastosowaniu konwersji przechwytywania (§ 5.1.1), a przede wszystkim w lub (T1, T2) .
Prostym językiem angielskim i po skrajnym uproszczeniu możemy opisać ten proces jako obliczenie „najmniejszej wspólnej superklasy” (tak, pomyśl o LCM) drugiego i trzeciego parametru. To da nam trójskładnikowy „typ”. Ponownie, to, co właśnie powiedziałem, jest ekstremalnym uproszczeniem (rozważ klasy, które implementują wiele popularnych interfejsów).
Na przykład, jeśli spróbujesz:
long millis = System.currentTimeMillis();
return(true ? new java.sql.Timestamp(millis) : new java.sql.Time(millis));
Zauważysz, że wynikowym typem wyrażenia warunkowego jest java.util.Date
to, że jest to „Najmniejsza wspólna nadklasa” dla pary Timestamp
/ Time
pair.
Ponieważ null
może być autoboxowane do czegokolwiek, „Least Common Superclass” jest Integer
klasą i będzie to zwracany typ wyrażenia warunkowego (operator trójskładnikowy) powyżej. Zwracana wartość będzie wtedy zerowym wskaźnikiem typu Integer
i to, co zostanie zwrócone przez operator trójskładnikowy.
W czasie wykonywania, gdy wirtualna maszyna Java się rozpakowuje, wyrzucane jest Integer
a NullPointerException
. Dzieje się tak, ponieważ JVM próbuje wywołać funkcję null.intValue()
, gdzie null
jest wynikiem autoboxowania.
Moim zdaniem (a ponieważ mojej opinii nie ma w specyfikacji języka Java, wiele osób i tak uzna to za błędne), kompilator źle radzi sobie z oceną wyrażenia w pytaniu. Biorąc pod uwagę, że napisałeś, true ? param1 : param2
kompilator powinien od razu ustalić, że pierwszy parametr - null
- zostanie zwrócony i powinien wygenerować błąd kompilatora. Jest to nieco podobne do tego, kiedy piszesz, while(true){} etc...
a kompilator narzeka na kod pod pętlą i oznacza go flagą Unreachable Statements
.
Druga sprawa jest dość prosta i ta odpowiedź jest już za długa ...;)
KOREKTA:
Po kolejnej analizie uważam, że nie miałem racji, twierdząc, że null
wartość może być spakowana / autoboxed do dowolnego elementu. Mówiąc o klasie Integer, jawne boksowanie polega na wywołaniu new Integer(...)
konstruktora, a może Integer.valueOf(int i);
(znalazłem tę wersję gdzieś). Pierwsze rzuciłoby NumberFormatException
(i tak się nie dzieje), a drugie nie miałoby sensu, ponieważ int
nie można null
...
int foo = (true ? null : 0)
inew Integer(null)
zarówno kompilacji porządku, druga jest wyraźne forma autoboxing.