次のコードから開始...
byte foo = 1;
byte fooFoo = foo + foo;
このコードをコンパイルしようとすると、次のエラーが表示されます...
エラー:(5、27)Java:互換性のない型:intからbyteへの損失の可能性のある変換
...ただし、foo
が最終の場合...
final byte foo = 1;
final byte fooFoo = foo + foo;
ファイルは正常にコンパイルされます。
次のコードに移ります...
final byte[] fooArray = new byte[1];
fooArray[0] = 1;
final byte foo = fooArray[0];
fooArray[0] = 127;
System.out.println("foo is: " + foo);
...印刷します
foo is: 1
...結構です。値は最終変数にコピーされ、変更できなくなります。配列の値を操作しても、foo
の値は変更されません(予想どおり...)。
次の場合、キャストが必要なのはなぜですか?
final byte[] fooArray = new byte[1];
fooArray[0] = 1;
final byte foo = fooArray[0];
final byte fooFoo = foo + foo;
これは、この質問の2番目の例とどう違うのですか?コンパイラから次のエラーが表示されるのはなぜですか?
エラー:(5、27)Java:互換性のない型:intからbyteへの損失の可能性のある変換
どうしてこれが起こりますか?
JLS( §5.2 )には、定数式を使用した代入変換のための特別なルールがあります。
また、式がタイプ
byte
、short
、char
、またはint
の定数式( §15.28 )の場合:
- 変数の型が
byte
、short
、またはchar
であり、定数式の値が変数の型で表現可能な場合、絞り込みプリミティブ変換を使用できます。
上記のリンクをたどると、これらはconstant expressionの定義に表示されます。
上記の2番目のリンクをたどると、
プリミティブ型またはタイプ
String
、つまりfinal
であり、コンパイル時の定数式( §15.28 )で初期化された変数は、定数変数と呼ばれます。
foo + foo
は、fooFoo
が定数変数である場合にのみfoo
に割り当てることができます。それをあなたのケースに適用するには:
byte foo = 1;
は、final
ではないため、定数変数を定義しません。
final byte foo = 1;
doesは、定数変数を定義します。これは、final
であり、定数式(プリミティブリテラル)。
final byte foo = fooArray[0];
は、定数式で初期化されないため、定数変数を定義しません。
fooFoo
自体がfinal
であるかどうかは重要ではありません。
値1は1バイトにうまく収まります。 1 + 1も同様です。そして、変数がfinalの場合、コンパイラーは 定数の折りたたみ を実行できます。 (言い換えると、コンパイラは、その+操作を実行するときにfoo
を使用しませんが、「raw」1値)
しかし、変数が最終的なものではない場合、変換とプロモーションに関するすべての興味深いルールが始まります( here を参照してください;プリミティブ変換の拡大についてはセクション5.12を読みたいです)。
2番目の部分では、配列をfinalにすることで、任意のフィールドをchangeできます。再び。一定の折りたたみはできません。そのため、「拡大」操作が再び開始されます。
実際、バイトコードからわかるように、コンパイラはfinal
を使用して定数畳み込みで実行します。
byte f = 1;
// because compiler still use variable 'f', so `f + f` will
// be promoted to int, so we need cast
byte ff = (byte) (f + f);
final byte s = 3;
// here compiler will directly compute the result and it know
// 3 + 3 = 6 is a byte, so no need cast
byte ss = s + s;
//----------------------
L0
LINENUMBER 12 L0
ICONST_1 // set variable to 1
ISTORE 1 // store variable 'f'
L1
LINENUMBER 13 L1
ILOAD 1 // use variable 'f'
ILOAD 1
IADD
I2B
ISTORE 2 // store 'ff'
L2
LINENUMBER 14 L2
ICONST_3 // set variable to 3
ISTORE 3 // store 's'
L3
LINENUMBER 15 L3
BIPUSH 6 // compiler just compute the result '6' and set directly
ISTORE 4 // store 'ss'
そして、最終バイトを127に変更すると、文句を言うでしょう:
final byte s = 127;
byte ss = s + s;
その場合、コンパイラーは結果を計算し、限界を超えていることを知っているので、互換性がないと文句を言います。
もっと:
そして、 here は、文字列を使用した定数の折りたたみに関する別の質問です。