型変換と昇格
型変換と昇格
このレッスンでできるようになること。byte や short の計算がなぜコンパイルエラーになるのかを説明でき、明示キャストで値がどう化けるかを計算できるようになります。
まず予測してください
次のコードのうち、コンパイルエラーになる行はどれでしょうか。
byte a = 10;
byte b = 20;
byte c = a + b;
byte d = 10 + 20;
byte e = 200;
short s = 1;
s = s + 1;
s += 1;答えと理由
エラーになるのは byte c = a + b; と byte e = 200; と s = s + 1; の3行です。
数値昇格
Java は byte、short、char どうしの算術演算を、いったん int に昇格させてから 計算します。だから a + b の型は byte ではなく int です。int を byte の変数に入れるには明示キャストが要るので、byte c = a + b; は「int を byte に変換できません」というエラーになります。値が 30 で範囲に収まっているかどうかは関係ありません。変数を使った時点で型の話になるのがポイントです。
定数畳み込み
一方 byte d = 10 + 20; は通ります。右辺がリテラルだけで組まれていて、コンパイル時に 30 と確定できるからです。この 30 は byte の範囲に収まっているので、コンパイラが黙って変換してくれます。byte e = 200; が通らないのは、200 が byte の範囲である -128 から 127 を超えているからです。同じリテラル代入でも範囲次第で結果が分かれます。
final が付いた変数も定数として扱われます。 final byte a = 10; final byte b = 20; byte c = a + b; は通ります。final の有無で結果がひっくり返る、というのが最上級の引っ掛けです。
複合代入は暗黙キャストを含みます
s = s + 1; はエラーですが、s += 1; は通ります。複合代入演算子にはキャストが内蔵されているからです。s += 1; は実質的に s = (short)(s + 1); と同じ意味に展開されます。同じように byte b = 10; b += 300; もコンパイルは通ってしまいます。エラーにならず、値が化けるだけです。
キャストで桁があふれると何が起きるか
明示キャストは、入りきらない上位ビットを黙って切り捨てます。例外は出ません。
(byte) 200は -56 です。200 は8ビットに収まらないので、下位8ビットだけが残り、先頭ビットが1なので負の数として読まれます。10進では 200 から 256 を引いた値になります(byte) 130は -126 です。130 から 256 を引いた値です(int) 3.99は 3 です。小数点以下は四捨五入ではなく切り捨てです(int) -3.99は -3 です。0 に近づく向きに切り捨てられるので、-4 ではありません
広がる向きは自動、狭まる向きは手動
情報が失われない向き、つまり byte から short、int、long、float、double へ向かう変換は自動です。逆向きは必ずキャストが要ります。例外的に注意したいのが char で、char は符号なしなので byte と char のあいだは両方向ともキャストが必要です。char c = 'A'; int n = c; は通りますが、char c2 = 65; byte b = c2; は通りません。
また long から float への変換は自動です。桁が落ちる可能性はありますが、Java は「表現できる範囲が広がる向き」を自動変換と決めているためです。ここも選択肢として狙われます。
まとめ
変数どうしの byte 演算は int になる、リテラルと final は定数として扱われる、複合代入はキャストを含む、キャストであふれた分は例外にならず黙って消えます。この4点が出題の核です。
要件
- byte 型の変数どうしの足し算の結果を byte 型の変数に収めること
- 200 と int 型の 130 を、それぞれ byte にキャストした値を求めること
- short 型の変数には複合代入演算子で 1 を足すこと
- 4つの値を半角スペース1つでつないだ文字列を返すこと
入出力例
convert() → "30 -56 -126 2"
convert() → "30 -56 -126 2"