まず、ローカル変数についてのみ説明します。事実上、finalはフィールドには適用されません。final
フィールドのセマンティクスは非常に明確であり、コンパイラの最適化とメモリモデルの約束の対象となるため、これは重要です。最終フィールドのセマンティクスについては、17.5.1ドルを参照してください。
表面レベルfinal
とeffectively final
ローカル変数は確かに同じです。ただし、JLSは、このような特殊な状況で実際に幅広い効果をもたらす2つを明確に区別します。
前提
変数についてのJLS§4.12.4からfinal
:
定数変数があるfinal
の変数プリミティブ型または型の文字列で初期化される定数式(§15.29)。変数が定数変数であるかどうかは、クラスの初期化(§12.4.1)、バイナリ互換性(§13.1)、到達可能性(§14.22)、および明確な割り当て(§16.1.1)に関して影響を与える可能性があります。
ためint
のプリミティブであり、変数はa
そのようなある一定の変数。
さらに、同じ章からeffectively final
:
最終として宣言されていない特定の変数は、代わりに事実上最終と見なされます。
だから、これは言葉で表現されている方法から、他の例では、ことは明らかであるa
されていないことがあるとして、一定の変数と考え、最終的ではないが、唯一効果的に、最終的な。
動作
区別ができたので、何が起こっているのか、なぜ出力が異なるのかを調べてみましょう。
? :
ここでは条件演算子を使用しているので、その定義を確認する必要があります。JLS§15.25から:
条件式には、ブール条件式、数値条件式、参照条件式の3種類があり、第2オペランド式と第3オペランド式で分類されます。
この場合、私たちは話している数値の条件式から、JLS§15.25.2:
数値条件式のタイプは、次のように決定されます。
そして、それは2つのケースが異なって分類される部分です。
事実上最終
effectively final
一致するバージョンは、次のルールに一致します。
それ以外の場合、一般的な数値昇格(§5.6)が2番目と3番目のオペランドに適用され、条件式の型は2番目と3番目のオペランドの昇格された型になります。
これは、実行する場合と同じ動作です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))が、メソッド(§12.5)を使用して一意のインスタンスを共有するように「インターン」されるためです。String.intern
主にリテラルについて話しているので見落としがちですが、実際には定数式にも当てはまります。