En primer lugar, estamos hablando solo de variables locales . Efectivamente final no se aplica a los campos. Esto es importante, ya que la semántica de los final
campos es muy distinta y está sujeta a grandes optimizaciones del compilador y promesas del modelo de memoria, consulte $ 17.5.1 sobre la semántica de los campos finales.
A nivel superficial final
y effectively final
para las variables locales son de hecho idénticas. Sin embargo, el JLS hace una clara distinción entre los dos, que en realidad tiene una amplia gama de efectos en situaciones especiales como esta.
Premisa
De JLS§4.12.4 sobre final
variables:
Una variable constante es una final
variable de tipo primitivo o tipo String que se inicializa con una expresión constante ( §15.29 ). Si una variable es una variable constante o no, puede tener implicaciones con respecto a la inicialización de la clase ( §12.4.1 ), la compatibilidad binaria ( §13.1 ), la accesibilidad ( §14.22 ) y la asignación definida ( §16.1.1 ).
Dado que int
es primitiva, la variable a
es una variable constante .
Además, del mismo capítulo sobre effectively final
:
Ciertas variables que no se declaran finales se consideran en cambio efectivamente finales: ...
Así que desde la forma en que esto está redactado, es claro que en el otro ejemplo, a
se no se considera una variable constante, ya que es no es definitivo , pero solamente con eficacia final.
Comportamiento
Ahora que tenemos la distinción, busquemos qué está pasando y por qué la salida es diferente.
Estás usando el operador condicional ? :
aquí, así que tenemos que verificar su definición. Desde JLS§15.25 :
Hay tres tipos de expresiones condicionales, clasificadas de acuerdo con la segunda y tercera expresiones de operando: expresiones booleanas condicionales , las expresiones condicionales numéricos y expresiones condicionales de referencia .
En este caso, estamos hablando de expresiones condicionales numéricas , de JLS§15.25.2 :
El tipo de expresión condicional numérica se determina de la siguiente manera:
Y esa es la parte en la que los dos casos se clasifican de manera diferente.
efectivamente final
La versión que effectively final
corresponde a esta regla:
De lo contrario, la promoción numérica general ( §5.6 ) se aplica al segundo y tercer operandos, y el tipo de expresión condicional es el tipo promovido del segundo y tercer operandos.
Cuál es el mismo comportamiento que si lo hiciera 5 + 'd'
, es decir int + char
, que resulta en int
. Ver JLS§5.6
La promoción numérica determina el tipo promocionado de todas las expresiones en un contexto numérico. El tipo promocionado se elige de modo que cada expresión se pueda convertir al tipo promovido y, en el caso de una operación aritmética, la operación se define para valores del tipo promocionado. El orden de las expresiones en un contexto numérico no es significativo para la promoción numérica. Las reglas son las siguientes:
[...]
A continuación, se aplican a algunas expresiones la conversión de primitivas de ampliación ( §5.1.2 ) y la conversión de primitivas de reducción ( §5.1.3 ), de acuerdo con las siguientes reglas:
En un contexto de elección numérica, se aplican las siguientes reglas:
Si alguna expresión es de tipo int
y no es una expresión constante ( §15.29 ), entonces el tipo promocionado es int
, y otras expresiones que no son de tipo se int
someten a una conversión primitiva amplia a int
.
Así que todo se promueve a int
que a
es un int
hecho. Eso explica la salida de 97
.
final
La versión con la final
variable coincide con esta regla:
Si uno de los operandos es de tipo T
donde T
es byte
, short
o char
, y el otro operando es una expresión constante ( §15.29 ) de tipo int
cuyo valor es representable en tipo T
, entonces el tipo de la expresión condicional es T
.
La variable final a
es de tipo int
y expresión constante (porque lo es final
). Es representable como char
, por lo tanto, el resultado es de tipo char
. Con eso concluye la salida a
.
Ejemplo de cadena
El ejemplo con la igualdad de cadenas se basa en la misma diferencia central, las final
variables se tratan como expresión / variable constante y effectively final
no lo es.
En Java, la internación de cadenas se basa en expresiones constantes, por lo tanto
"a" + "b" + "c" == "abc"
es true
también (no use esta construcción en código real).
Consulte JLS§3.10.5 :
Además, un literal de cadena siempre se refiere a la misma instancia de la clase String. Esto se debe a que los literales de cadena - o, más generalmente , cadenas que son los valores de expresiones constantes ( §15.29 ) - están "internados" para compartir instancias únicas, usando el método String.intern
( §12.5 ).
Fácil de pasar por alto, ya que se trata principalmente de literales, pero en realidad también se aplica a expresiones constantes.