結論: 範囲が広い型から狭い型への代入(縮小プリミティブ変換)は、(int) のような明示的なキャストを書かないとコンパイルエラーになります。キャストを書けばコンパイルは通りますが、小数部の切り捨てや上位ビットの切り捨てで値が変わることがあります。例外として、コンパイル時に値が決まる byte・short・char・int 型の定数が変換先の範囲に収まる場合は、byte・short・char へキャストなしで代入できます。
キャストが必要な代表例と結果
| 変換元 → 変換先 | 書き方 | 結果 | 何が起きるか |
|---|---|---|---|
double → int |
(int) 3.7 |
3 |
小数部を切り捨て(0 方向) |
double → int |
(int) -3.7 |
-3 |
負数も 0 方向に切り捨て |
long → int |
(int) 3_000_000_000L |
-1294967296 |
下位 32 ビットだけが残る |
int → byte |
(byte) 200 |
-56 |
下位 8 ビットだけが残る |
int → char |
(char) 65 |
'A' |
下位 16 ビットを文字コードとして解釈 |
double → float |
(float) 0.1 |
0.1f |
float の精度に丸める |
short と char の間、byte から char への変換も、キャストが必要です。
コード例
// int x = 3.7; // コンパイルエラー: double から int への変換で精度が失われる可能性
int x = (int) 3.7; // OK: x は 3
long l = 10L;
// int n = l; // コンパイルエラー: long から int への縮小変換は定数でもキャストが必要
int n = (int) l; // OK
byte b1 = 10; // OK: 定数 10 は byte の範囲(-128〜127)に収まる
// byte b2 = 128; // コンパイルエラー: 範囲外
char c = 65; // OK: 定数 65 は char の範囲に収まる('A')
int i = 10;
// byte b3 = i; // コンパイルエラー: i は定数ではない
final int k = 10;
byte b4 = k; // OK: final で定数で初期化した変数は定数扱い
byte b5 = 10;
b5 += 1; // OK: 複合代入は暗黙にキャストを含む
// b5 = b5 + 1; // コンパイルエラー: b5 + 1 の型は int
原理(なぜそうなるか)
縮小変換では、変換先の型が元の値を表せないことがあり、値が黙って変わる危険があります。そのため Java は、プログラマが (型) と書いて「値が変わってもよい」と明示した場合にだけ変換を許します。
定数の代入特例(JLS 5.2)は、コンパイラが値を確定できるときに限って安全を確かめられるから成り立っています。byte b = 10; の 10 は int 型のリテラルですが、値が byte の範囲内だとコンパイル時に分かるので代入を認めます。変数 i の値は実行するまで分からないので、byte b = i; は認められません。この特例の対象は byte・short・char への代入だけで、int i = 10L; のような long 定数からの代入は通りません。
b5 += 1 が通るのは、複合代入 E1 op= E2 が E1 = (E1の型) (E1 op E2) と定義されているためです(JLS 15.26.2)。
(この初版はAIが作成。随時書き換え)