Zunächst sprechen wir nur über lokale Variablen . Tatsächlich gilt final nicht für Felder. Dies ist wichtig, da die Semantik für final
Felder sehr unterschiedlich ist und umfangreichen Compiler-Optimierungen und Versprechungen für das Speichermodell unterliegt (siehe 17.5.1 USD zur Semantik der endgültigen Felder).
Auf Oberflächenebene final
und effectively final
für lokale Variablen sind in der Tat identisch. Das JLS unterscheidet jedoch klar zwischen den beiden, was in solchen besonderen Situationen tatsächlich eine Vielzahl von Auswirkungen hat.
Prämisse
Aus JLS§4.12.4 über final
Variablen:
Eine konstante Variable ist eine final
Variable vom primitiven Typ oder Typ String , die mit einem konstanten Ausdruck initialisiert wird ( §15.29 ). Ob eine Variable eine konstante Variable ist oder nicht, kann Auswirkungen auf die Klasseninitialisierung ( §12.4.1 ), die Binärkompatibilität ( §13.1 ), die Erreichbarkeit ( §14.22 ) und die definitive Zuordnung ( §16.1.1 ) haben.
Da int
es primitiv ist, ist die Variable a
eine solche konstante Variable .
Weiter aus dem gleichen Kapitel über effectively final
:
Bestimmte Variablen, die nicht als endgültig deklariert sind, werden stattdessen als effektiv endgültig betrachtet: ...
So aus dem Weg , dies formuliert wird, ist es klar , dass in dem anderen Beispiel, a
ist nicht eine konstante Größe betrachtet, wie es ist nicht endgültig , sondern nur effektiv endgültig.
Verhalten
Nachdem wir nun die Unterscheidung getroffen haben, schauen wir nach, was los ist und warum die Ausgabe anders ist.
Sie verwenden hier den bedingten Operator ? :
, daher müssen wir seine Definition überprüfen. Aus JLS§15.25 :
Es gibt drei Arten von bedingten Ausdrücken, die nach dem zweiten und dritten Operandenausdruck klassifiziert sind: boolesche bedingte Ausdrücke , numerische bedingte Ausdrücke und bedingte Referenzausdrücke .
In diesem Fall handelt es sich um numerische bedingte Ausdrücke aus JLS§15.25.2 :
Der Typ eines numerischen bedingten Ausdrucks wird wie folgt bestimmt:
Und das ist der Teil, in dem die beiden Fälle unterschiedlich klassifiziert werden.
effektiv endgültig
Die Version, effectively final
die dieser Regel entspricht:
Andernfalls wird die allgemeine numerische Heraufstufung ( §5.6 ) auf den zweiten und dritten Operanden angewendet, und der Typ des bedingten Ausdrucks ist der heraufgestufte Typ des zweiten und dritten Operanden.
Welches ist das gleiche Verhalten, als ob Sie tun würden 5 + 'd'
, dh int + char
was zu führt int
. Siehe JLS§5.6
Die numerische Heraufstufung bestimmt den heraufgestuften Typ aller Ausdrücke in einem numerischen Kontext. Der heraufgestufte Typ wird so gewählt, dass jeder Ausdruck in den heraufgestuften Typ konvertiert werden kann, und im Fall einer arithmetischen Operation wird die Operation für Werte des heraufgestuften Typs definiert. Die Reihenfolge der Ausdrücke in einem numerischen Kontext ist für die numerische Werbung nicht von Bedeutung. Die Regeln lauten wie folgt:
[...]
Als nächstes werden die Erweiterung der primitiven Konvertierung ( §5.1.2 ) und die Verengung der primitiven Konvertierung ( §5.1.3 ) auf einige Ausdrücke gemäß den folgenden Regeln angewendet:
In einem numerischen Auswahlkontext gelten die folgenden Regeln:
Wenn ein Ausdruck vom Typ ist int
und kein konstanter Ausdruck ist ( §15.29 ), ist der heraufgestufte Typ int
und andere Ausdrücke, die nicht vom Typ sind, werden int
einer erweiterten primitiven Konvertierung in unterzogen int
.
So wird alles so gefördert, int
wie a
es int
schon ist. Das erklärt die Ausgabe von 97
.
Finale
Die Version mit der final
Variablen entspricht dieser Regel:
Wenn einer der Operanden - Typ ist , T
wo T
ist byte
, short
oder char
, und der andere Operand ein konstanter Ausdruck ( §15.29 ) des Typs , int
dessen Wert in darstellbare Typ T
, dann wird der Typ des Bedingungsausdrucks ist T
.
Die letzte Variable a
ist vom Typ int
und ein konstanter Ausdruck (weil es so ist final
). Es ist darstellbar als char
, daher ist das Ergebnis vom Typ char
. Damit ist die Ausgabe abgeschlossen a
.
String-Beispiel
Das Beispiel mit der Zeichenfolgengleichheit basiert auf demselben Kernunterschied, final
Variablen werden als konstanter Ausdruck / Variable behandelt und effectively final
nicht.
In Java basiert die String-Internierung daher auf konstanten Ausdrücken
"a" + "b" + "c" == "abc"
ist true
auch (verwenden Sie dieses Konstrukt nicht in echtem Code).
Siehe JLS§3.10.5 :
Darüber hinaus bezieht sich ein String-Literal immer auf dieselbe Instanz der Klasse String. Dies liegt daran, dass Zeichenfolgenliterale - oder allgemeiner Zeichenfolgen, die die Werte konstanter Ausdrücke sind ( §15.29 ) - "interniert" werden, um mithilfe der Methode String.intern
( §12.5 ) eindeutige Instanzen gemeinsam zu nutzen .
Leicht zu übersehen, da es sich hauptsächlich um Literale handelt, aber es gilt auch für konstante Ausdrücke.