第 2 章 Java 言語の基本的な文法 I 5
2.11 キャスト
c = b = a = 1;
System.out.println( "a=" + a + ", b=" + b + ", c=" + c );
} }
先ほどの「式の中の代入文」の考え方で、c = b = a = 1;は、まず右から順に代入文を読んでいき、aの値がまず1 に、次にb = (a = 1) からbも1に、そしてc = (b = 1) よりcも1になるというわけです。でも、このプログ ラムは、以下のプログラムと同じですね。(^^;)
ソースコード2.18 MultipleSubst2.java (多重代入文は使わなくても) package section0210;
public class MultipleSubst2 {
public static void main(String[] args) { int a = 1;
int b = 1;
int c = 1;
System.out.println( "a=" + a + ", b=" + b + ", c=" + c );
} }
なお、c = b*2 = a = 1;とか、c = b++ = a = 1;なんてのはできません。理由はわかるかな?
問題 2.10.9. 多重代入文に複合代入文演算子も使えます。以下のプログラムの出力結果を書きなさい。
ただし、このプログラムもあくまで問題用ですね。
ソースコード2.19 MultipleSubst3.java (複雑な多重代入文) package section0210;
public class MultipleSubst3 {
public static void main(String[] args) { int x = 2;
int y = 3;
int z = 4;
z += y *= x;
System.out.println( "x=" + x + ", y=" + y + ", z=" + z );
} }
値のみ型変換してくれるので、例えばint y = (int) 2.0 + 1.0;はエラーになります。なぜでしょう?
キャスト(int)が2.0にのみに掛かるので、実質int y = 2 + 1.0;と見なされて、右辺の計算結果はdouble型の 3.0、これはint型に自動変換されません。この場合、int y = (int)(2.0 + 1.0);とキャストしたい部分をかっこ でくくってやれば、キャストの前に2 つの実数の和を計算するので上手くいきます(このときyには3 が入ります)。 キャストはリテラルだけでなく変数でも可能です。double型変数xに格納されている値をint型変数yに代入し たい場合に、y = (int) x;とすれば良いのです。ところで、xに今1.5が格納されているとしたら、yにはキャスト の結果いくつの値が入るのでしょう。それは、キャスト先のyの型に合わせて情報が切り捨てられます。つまり、1.5 の余分な小数点以下の値が切り捨てられて、1が代入されるわけです。
問題 2.11.1. 以下の代入文で、エラーとなるものはどれですか。キャストが付いているが、実はキャストせずとも代
入できるのはどれですか。
1. int x = (int) 2.5 - 1; 2. int x = 1.5 - 0.5; 3. float x = (float) 123.45;
4. int x = (int) 12345L; 5. long x = (long) 123; 6. double x = (double) 12.3F;
7. byte x = (byte) 100; 8. long x = (long) 1234; 9. char x = (char) 12354;
10. int x = (int) true; 11. int x = (int) 'A';
上の設問 7 では、int型の値を表現できる範囲の狭いshort, byte 型の変数に代入するには、ルール通りなら、
キャストせずに代入できないはずですが、先にも触れたように、 int より表現できる範囲の狭い short, byte 型 には特別ルールとして表現できる範囲内の整数リテラルであればキャストなしで代入できるようになっています。
つまり、byte x = 100; は 100 という値が、byte 型変数の表現できる [−128,127]間の整数値になっていること から特別ルールで OK となります。ただし、 int a = 100; byte x = a; はエラーになります。この場合は、
int a = 100; byte x = (byte) a; としなければいけません。リテラルは特別ルールが効くけれど、変数はダメ ということですね。何故でしょう?変数 aには今たまたま100 が入るだけであって、範囲外の値も入る可能性がある からですね。従って、byte x = 200; byte x = (byte) a;はエラーです。200 が区間[−128,127]に含まれない からです。でも、以下のプログラムはエラーを出しません。確かめてみて下さい。
ソースコード2.20 Casting1.java (ムリやりキャスト) package section0211;
public class Casting1 {
public static void main(String[] arge) { byte x = (byte) 200;
System.out.println( "x = " + x );
} }
動きましたが、値が変ですよね。このキャストという操作をもう少し細かく見てみましょう。200 というリテラルは int型ですから、Javaでは32bits符号付き整数として表されています。一方、byte 型変数xは8bits符号付き整数 として記憶領域が確保されています。そこで、キャスト(byte)が行われると、32bitsの情報の下位8bitsを残して上
位24bits の情報を捨ててしまうのです(むちゃくちゃです。符号くらい合わせても良いと思うのですがね)。よって、
xには−56なる値が代入されるというわけです。
byte x = 100;の場合も図2.11のような図を書いて確かめて下さい。
ということで、キャストで想定外の値になる可能性もあることを知っておいて下さい。この辺り、Java はまだ低レ ベルです。
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 32bits 符号付き整数の 200
1 1 0 0 1 0 0 0
8bits 符号付き整数 -56 (2の補数表現に注意)
キャスト (byte)
図2.12 200をbyte型にキャストすると
問題 2.11.2. byte 型で表現できる整数の範囲 [−128,127]の境界の値で、キャスト (byte)の結果(xの値)がいく つになるか求めよ。
1. byte x = (byte) -129; 2. byte x = (byte) -128;
3. byte x = (byte) 127; 4. byte x = (byte) 128;
キャストの話はまだ続きます。次のプログラムを実行してみましょう。
ソースコード2.21 Casting2.java (丸め誤差とキャスト) package section0211;
public class Casting2 {
public static void main(String[] arge) { double x = 10.1;
int y = (int)( x / 0.1 );
System.out.println( "y = " + y );
} }
y = 101と出力されると思いきや、y = 100と出力されます。なぜでしょう?
理由を知るために、5行目と 6行目の間にSystem.out.println( x / 0.1 );と入れてみて下さい。101.0と出力 されるはずが、100.99999999999999と誤差を持って表示されます。従って、それをint型にキャストすれば、小数 点以下が切り捨てられて、100となるわけです。このように、キャストは丸め誤差が起こっているかも?にも気を付け ねばいけません。とはいえ、便利な道具ではあります。
問題 2.11.3. 実数変数xに入っている値を整数変数yへ、小数点以下を四捨五入して代入する処理をキャストを利用
して書きなさい。(ヒント:キャストはあくまで切り捨てなので、0.5加えてから. . .) なお、後で、Math.round()という四捨五入メソッドを習います。
キャストの最後に、教科書にあまり載っていない複合代入文とキャストにまつわる話を。次のプログラム、エラーな く動いてx = 2を出力します。でも、どこか違和感ありませんか?複合代入文とはいえ int型の変数xにdouble型 リテラル 2.5 を代入できている?のです。
ソースコード2.22 CompoundAssign.java (複合代入文での隠れキャスト)
package section0211;
public class CompoundAssign {
public static void main(String[] arge) { int x = 0;
x += 2.5;
System.out.println( "x = " + x );
} }
ちなみに、x += 2.5;の部分を x = x + 2.5; と書き換えるとコンパイルエラーとなります。つまり、この2つの 処理に違いがあるということです。実は複合代入文では、裏でキャストが行われているのです。この場合は、x =
(int)( x + 2.5 );と左辺の変数の型にキャストする処理が隠れているのです。和田も、ちょっと前までこのことに
気づいていませんでした (まだまだ知らないことがあるんだなぁ)。しっかりマニュアルを読まずにプログラミング言 語を使うと、思わぬミスが隠れているかもしれないということですね。ちなみに、こんなプログラムは書かないで。読 む人を混乱させるだけです。
問題 2.11.4. 以下の代入文で変数aの値はそれぞれ幾つになりますか。
1. double a = 4 + 3 / 2 + 2; 2. double a = 4 + 3.0 / 2 + 2;
3. double a = (4 + 3) / 2 + 2; 4. double a = (double) 4 + 3 / 2 + 2;
5. double a = (double)(4 + 3) / 2 + 2; 6. double a = 4 + 3 / (2 + 2.0);