Перш за все, мова йде лише про локальні змінні . Фактично остаточний не застосовується до полів. Це важливо, оскільки семантика final
полів дуже чітка і піддається жорсткій оптимізації компілятора та обіцянкам моделі пам'яті, див. $ 17.5.1 щодо семантики кінцевих полів.
На поверхневому рівні final
та effectively final
для локальних змінних дійсно однакові. Однак JLS чітко розмежовує їх, що насправді має широкий спектр ефектів у таких особливих ситуаціях.
Приміщення
З JLS§4.12.4 про final
змінні:
Мінлива константа є final
змінною примітивного типу або типу String , що інстанціюється з постійним виразом ( §15.29 ). Чи є змінна постійною змінною, чи ні, це може мати наслідки щодо ініціалізації класу ( §12.4.1 ), бінарної сумісності ( §13.1 ), доступності ( §14.22 ) та певного призначення ( §16.1.1 ).
Оскільки int
примітивна, змінна a
є такою постійною змінною .
Далі, з тієї ж глави про effectively final
:
Деякі змінні, які не оголошені остаточними, натомість вважаються фактично остаточними: ...
Таким чином , з образом це сформульовано, то ясно , що в іншому прикладі, a
це НЕ вважається постійної змінної, так як це НЕ є остаточним , але тільки ефективно остаточним.
Поведінка
Тепер, коли ми маємо різницю, давайте подивимося, що відбувається і чому результат виходить інший.
Ви використовуєте умовний оператор ? :
тут, тому ми повинні перевірити його визначення. З JLS§15.25 :
Існує три типи умовних виразів, класифікованих відповідно до виразів другого та третього операндів: логічні умовні вирази , числові умовні вирази та посилальні умовні вирази .
У цьому випадку ми говоримо про числові умовні вирази з JLS§15.25.2 :
Тип числового умовного виразу визначається наступним чином:
І це частина, де ці два випадки класифікуються по-різному.
фактично остаточний
Версія, яка effectively final
відповідає цьому правилу:
В іншому випадку загальне числове просування ( §5.6 ) застосовується до другого та третього операндів, а тип умовного виразу - це підвищений тип другого та третього операндів.
Це така сама поведінка, як якщо б ви робили 5 + 'd'
, тобто int + char
, що призводить до int
. Див. JLS§5.6
Числове просування визначає тип просування всіх виразів у числовому контексті. Тип, що рекламується, вибирається таким чином, що кожен вираз може бути перетворений у тип, що рекламується, і, у випадку арифметичної операції, операція визначена для значень рекламного типу. Порядок виразів у числовому контексті не є значущим для числового просування. Правила такі:
[...]
Далі, розширення примітивного перетворення ( §5.1.2 ) та звуження примітивного перетворення ( §5.1.3 ) застосовуються до деяких виразів, згідно з наступними правилами:
У контексті числового вибору застосовуються такі правила:
Якщо будь-який вираз має тип int
і не є постійним виразом ( §15.29 ), тоді промотований тип є int
, а інші вирази, що не мають типу, int
зазнають розширення примітивного перетворення в int
.
Отже, все просувається до того int
, a
що int
вже є. Це пояснює результат 97
.
остаточний
Версія зі final
змінною відповідає цьому правилу:
Якщо один з операндів має типу , T
де T
знаходиться byte
, short
або char
, а інший операндом є постійним виразом ( §15.29 ) типу int
, значення якого представимо в типі T
, то тип умовного виразу T
.
Кінцева змінна a
має тип int
і постійний вираз (оскільки вона є final
). Це можна представити як char
, отже, результат є типовим char
. На цьому результат закінчується a
.
Приклад рядка
Приклад з рівністю рядків базується на тій же різниці ядра, final
змінні трактуються як константний вираз / змінна, і effectively final
це не так.
У Java розпізнавання рядків базується на константних виразах, отже
"a" + "b" + "c" == "abc"
є true
також (не використовуйте цю конструкцію в реальному коді).
Див. JLS§3.10.5 :
Більше того, строковий літерал завжди посилається на один і той же екземпляр класу String. Це пояснюється тим, що рядкові літерали - або, загальніше , рядки, що є значеннями константних виразів ( §15.29 ) - "інтернуються" , щоб спільно використовувати унікальні екземпляри, використовуючи метод String.intern
( §12.5 ).
Легко не помітити, оскільки мова йде переважно про літерали, але насправді це стосується і постійних виразів.