Przede wszystkim mówimy tylko o zmiennych lokalnych . Skutecznie ostateczna nie dotyczy pól. Jest to ważne, ponieważ semantyka final
pól jest bardzo różna i podlega ciężkim optymalizacjom kompilatora i obietnicom modelu pamięci, patrz 17.5.1 na temat semantyki pól końcowych.
Na poziomie powierzchniowym final
i effectively final
dla zmiennych lokalnych są rzeczywiście identyczne. Jednak JLS dokonuje wyraźnego rozróżnienia między nimi, co w rzeczywistości ma szeroki zakres efektów w specjalnych sytuacjach, takich jak ta.
Przesłanka
Od JLS§4.12.4 o final
zmiennych:
Zmiennej stałej jest final
zmienne od pierwotnego typu lub typu ciąg , który jest inicjowany przy stałej ekspresji ( §15.29 ). To, czy zmienna jest zmienną stałą, czy nie, może mieć wpływ na inicjalizację klasy ( §12.4.1 ), zgodność binarną ( §13.1 ), osiągalność ( §14.22 ) i określone przypisanie ( §16.1.1 ).
Ponieważ int
jest prymitywna, zmienna a
jest taką stałą zmienną .
Dalej, z tego samego rozdziału o effectively final
:
Niektóre zmienne, które nie zostały uznane za ostateczne, są zamiast tego uważane za faktycznie ostateczne:
Ze sposobu sformułowania tego wynika jasno, że w drugim przykładzie a
nie jest uważana za zmienną stałą, ponieważ nie jest ostateczna , a jedynie faktycznie ostateczna.
Zachowanie
Teraz, gdy mamy rozróżnienie, spójrzmy, co się dzieje i dlaczego wynik jest inny.
Używasz operatora warunkowego ? :
tutaj , więc musimy sprawdzić jego definicję. Od JLS§15.25 :
Istnieją trzy rodzaje wyrażeń warunkowych, sklasyfikowanych zgodnie z drugim i trzecim wyrażeniem operandowym : logiczne wyrażenia warunkowe , numeryczne wyrażenia warunkowe i wyrażenia warunkowe dotyczące odwołań .
W tym przypadku mówimy o pliku liczbowych wyrażeniach warunkowych z JLS§15.25.2 :
Typ liczbowego wyrażenia warunkowego jest określany w następujący sposób:
I to jest ta część, w której te dwa przypadki są inaczej klasyfikowane.
skutecznie ostateczne
Wersja, do której effectively final
pasuje ta reguła:
W przeciwnym razie ogólna promocja liczbowa ( §5.6 ) jest stosowana do drugiego i trzeciego operandu, a typ wyrażenia warunkowego to promowany typ drugiego i trzeciego operandu.
Jest to takie samo zachowanie, jak gdybyś to zrobił 5 + 'd'
, tj. W int + char
wyniku czego int
. Zobacz JLS§5.6
Promocja numeryczna określa promowany typ wszystkich wyrażeń w kontekście liczbowym. Promowany typ jest wybierany w taki sposób, że każde wyrażenie można przekonwertować na promowany typ, aw przypadku operacji arytmetycznej operacja jest zdefiniowana dla wartości typu promowanego. Kolejność wyrażeń w kontekście liczbowym nie ma znaczenia dla promocji liczbowej. Zasady są następujące:
[…]
Następnie do niektórych wyrażeń stosuje się rozszerzającą konwersję pierwotną ( pkt 5.1.2 ) i zwężającą konwersję pierwotną (pkt 5.1.3 ), zgodnie z następującymi regułami:
W kontekście wyboru liczbowego obowiązują następujące reguły:
Jeśli jakiekolwiek wyrażenie jest typu int
i nie jest wyrażeniem stałym ( §15.29 ), to promowany typ jest int
, a inne wyrażenia, które nie są typu, int
są poddawane rozszerzającej konwersji pierwotnej do int
.
Więc wszystko jest promowane int
tak, jak a
jest int
już. To wyjaśnia wydajność 97
.
finał
Wersja ze final
zmienną jest dopasowana do tej reguły:
Jeśli jeden z operandów jest typu, T
gdzie T
jest byte
, short
lub char
, a drugi operand jest wyrażeniem stałym ( §15.29 ) typu, int
którego wartość można przedstawić w typie T
, to typ wyrażenia warunkowego to T
.
Ostatnia zmienna a
jest typu int
i wyrażeniem stałym (ponieważ jest final
). Można to przedstawić jako char
, stąd wynik jest typowy char
. Na tym kończy się wynik a
.
Przykład ciągu
Przykład z równością ciągów opiera się na tej samej podstawowej różnicy, final
zmienne są traktowane jako stałe wyrażenie / zmienna, a effectively final
tak nie jest.
Dlatego w Javie internowanie ciągów opiera się na stałych wyrażeniach
"a" + "b" + "c" == "abc"
jest true
również (nie używaj tej konstrukcji w prawdziwym kodzie).
Zobacz JLS §3.10.5 :
Ponadto literał łańcuchowy zawsze odnosi się do tego samego wystąpienia klasy String. Dzieje się tak, ponieważ literały łańcuchowe - lub, bardziej ogólnie , łańcuchy, które są wartościami wyrażeń stałych ( §15.29 ) - są „internowane” w celu współużytkowania unikalnych instancji przy użyciu metody String.intern
( §12.5 ).
Łatwo przeoczyć, ponieważ mówi głównie o literałach, ale w rzeczywistości dotyczy to również stałych wyrażeń.