【Javaの基礎】Javaのキャスト

 

こんにちは、現役SEの「とも」です。

今回は、キャストについて説明します。

 

  • キャストとは、変数やリテラルのデータ型を、別のデータ型に無理やり変換することを言います。

無理やりなので、使い方を理解していないと想定外の結果となる場合があります

 

キャストは以下のデータ型間でできます。

「継承関係のあるクラス間」についてはクラスについての時に説明しますので、今の時点では割愛させていただきます。

 

  • 整数型
  • 浮動小数点型

 

  • 継承関係のあるクラス間

 

キャストの記載方法は以下のように、変数の前にカッコつきで変換したい型を記載します。

 
int a = 127;
byte b = (byte)a; //intからbyteへキャスト
	

 

整数型どうしのキャスト

整数型は、byte→short→int→longの順に表現できる数値が大きくなりますよね。

整数型どうしで行うキャストでは、表現できる数値を意識して行う必要があります。

 

大きいデータ型 → 小さいデータ型にキャスト

小さいデータ型では、表現できる数値に限りがあります。

大きいデータ型が小さいデータ型で表現できる範囲であれば、そのままの値となりますが、大きな値の場合、以下のような切り捨てが行われます

 

 
int a = 127; // byteで表現できる上限値
byte b = (byte)a; //intからbyteへキャスト
int c = 128; // byteで表現できる上限値 + 1
byte d = (byte)c; //intからbyteへキャスト
int e = 256; // 9ビット目が1の場合の数値
byte f = (byte)e; //intからbyteへキャスト

System.out.println("キャスト前:" + a + "キャスト後:" + b);
System.out.println("キャスト前:" + c + "キャスト後:" + d);
System.out.println("キャスト前:" + e + "キャスト後:" + f);
	

 

【結果】

 
キャスト前:127キャスト後:127
キャスト前:128キャスト後:-128
キャスト前:256キャスト後:0
	

 

なぜこのようになるのかというと、以下の図のように2進数で見たときにキャスト前とキャスト後で上位のビットが切り捨てられてしまうからです。

下の図は分かりやすくするためにint型からbyte型へのキャストを例としてますが、大きな整数型から小さな整数型へのキャストは、どのデータ型でもこのような動きとなります。

 

【int型で127の時】

整数型のキャスト1(切り捨て)

 

【int型で128の時】

整数型のキャスト2(切り捨て)

 

【int型で256の時】

整数型のキャスト3(切り捨て)

 

小さいデータ型 → 大きいデータ型にキャスト

逆に、小さいデータ型から大きいデータ型にキャストした場合はどうなるかというと、データ型が大きくなるため、保持している値はそのままの状態でデータ型が変換されます

 

また、明示的にキャストを指定しなくてもデータ型はJavaが自動的に変換してくれます

Javaが自動的にキャストしてくれることを、「暗黙的なキャスト」といいます。

 

 
byte a = 127; // byteで表現できる上限値
int b = (int)a; //byteからintへ明示的なキャスト
int c = a; //byteからintへ暗黙的なキャスト
byte d = -128; // byteで表現できる上限値
int e = (int)d; //byteからintへ明示的なキャスト
int f = d; //byteからintへ暗黙的なキャスト

System.out.println("キャスト前:" + a + "\tキャスト後:" + b);
System.out.println("キャスト前:" + a + "\tキャスト後:" + c);
System.out.println("キャスト前:" + d + "\tキャスト後:" + e);
System.out.println("キャスト前:" + d + "\tキャスト後:" + f);
	

 

【結果】

 
キャスト前:127 キャスト後:127
キャスト前:127 キャスト後:127
キャスト前:-128 キャスト後:-128
キャスト前:-128 キャスト後:-128
	

 

浮動小数点型どうしのキャスト

浮動小数点型は、float→doubleの順に表現できる精度(桁数)が良くなってきます

浮動小数点型どうしのキャストで気を付けるポイントを説明します。

 

大きいデータ型 → 小さいデータ型にキャスト

浮動小数点型どうしで行うキャストでは、大きなデータ型(double)から小さなデータ型(float)に変換する際、floatで表現できる精度(桁数)まで切り捨てられます

 

 
double a = 1.2345678; // floatで表現できる上限桁の数値
float b = (float)a; //doubleからfloatへキャスト
double c = 1.23456789987654321; // floatで表現できる上限を超える数値
float d = (float)c; //doubleからfloatへキャスト
double e = 1234567.8; // floatで表現できる上限桁の数値
float f = (float)e; //doubleからfloatへキャスト
double g = 12345678998765432.1; // floatで表現できる上限を超える数値
float h = (float)g; //doubleからfloatへキャスト

System.out.println("キャスト前:" + a + "\t\t\t\tキャスト後:" + b);
System.out.println("キャスト前:" + c + "\tキャスト後:" + d);
System.out.println("キャスト前:" + e + "\t\t\t\tキャスト後:" + f);
System.out.println("キャスト前:" + g + "\tキャスト後:" + h);
	

 

【結果】

 
キャスト前:1.2345678 キャスト後:1.2345678
キャスト前:1.2345678998765433 キャスト後:1.2345679
キャスト前:1234567.8 キャスト後:1234567.8
キャスト前:1.2345678998765432E16 キャスト後:1.234568E16
	

 

小さいデータ型 → 大きいデータ型にキャスト

 

浮動小数点型も整数型と同様に、小さいデータ型から大きいデータ型にキャストする場合は保持している値はそのままの状態でデータ型が変換されます。(値によっては変換時に誤差が発生する)

 

また、整数型と同様に明示的にキャストをしなくても、暗黙的なキャストがされます

 

 
float a = 1.2345678f;
double b = (double)a; //floatからdoubleへ明示的なキャスト
double c = a; //floatからdoubleへ暗黙的なキャスト
float d = -1.2345678f; //マイナス値
double e = (double)d; //floatからdoubleへ明示的なキャスト
double f = d; //floatからdoubleへ暗黙的なキャスト

System.out.println("キャスト前:" + a + "\t\tキャスト後:" + b);
System.out.println("キャスト前:" + a + "\t\tキャスト後:" + c);
System.out.println("キャスト前:" + d + "\t\tキャスト後:" + e);
System.out.println("キャスト前:" + d + "\t\tキャスト後:" + f);
	

 

【結果】

 
キャスト前:1.2345678 キャスト後:1.2345677614212036
キャスト前:1.2345678 キャスト後:1.2345677614212036
キャスト前:-1.2345678 キャスト後:-1.2345677614212036
キャスト前:-1.2345678 キャスト後:-1.2345677614212036
	

 

整数型 → 浮動小数点型へのキャスト

整数型から浮動小数点型へのキャストは明示的に指定する必要があります

整数型から浮動小数点型へのキャストの場合、桁数が浮動小数点型で表現できる範囲であれば切り捨ては行われませんが、表現できる範囲を超えたら切り捨てが行われます

 

 
long a = 1234567890123456L; 
double b = (double)a; //longからdoubleへキャスト
long c = 12345678L; 
float d = (float)c; //longからfloatへキャスト
long a1 = 1234567890123456789L; 
double b1 = (double)a1; //longからdoubleへキャスト
long c1 = 1234567890123456789L; 
float d1 = (float)c1; //longからfloatへキャスト

System.out.println("--- 切り捨てられないキャスト ---");
System.out.println("キャスト前:" + a + "\tキャスト後:" + b);
System.out.println("キャスト前:" + c + "\t\t\tキャスト後:" + d);
System.out.println("--- 切り捨てられるキャスト ---");
System.out.println("キャスト前:" + a1 + "\tキャスト後:" + b1);
System.out.println("キャスト前:" + c1 + "\tキャスト後:" + d1);
	

 

【結果】

 
--- 切り捨てられないキャスト ---
キャスト前:1234567890123456 キャスト後:1.234567890123456E15
キャスト前:12345678 キャスト後:1.2345678E7
--- 切り捨てられるキャスト ---
キャスト前:1234567890123456789 キャスト後:1.2345678901234568E18
キャスト前:1234567890123456789 キャスト後:1.234568E18
	

 

浮動小数点型 → 整数型へのキャスト

浮動小数点型から整数型へのキャストも明示的に指定する必要があります

 

浮動小数点型から整数型へのキャストの場合、小数点以下は切り捨てが行われます

また、整数型で表現できるよりも大きな値の場合も切り捨てが行われます

整数部については、大きな整数型から小さな整数型へキャストした場合と同様の動作となっています。

 

 
double a = 127.567; 
byte b = (byte)a; //doubleからbyteへキャスト(小数点以下切り捨て)
double c = 127; 
byte d = (byte)c; //doubleからbyteへキャスト(切り捨てなし)
double e = 128; 
byte f = (byte)e; //doubleからbyteへキャスト(切り捨てなし)
double g = 256; 
byte h = (byte)g; //doubleからbyteへキャスト(切り捨てなし)
double i = 256.123; 
byte j = (byte)i; //doubleからbyteへキャスト(切り捨てなし)

System.out.println("--- 小数点以下切り捨て ---");
System.out.println("キャスト前:" + a + "\tキャスト後:" + b);
System.out.println("--- 切り捨てなし ---");
System.out.println("キャスト前:" + c + "\tキャスト後:" + d);
System.out.println("--- 表現できるより大きな値(128) ---");
System.out.println("キャスト前:" + e + "\tキャスト後:" + f);
System.out.println("--- 表現できるより大きな値(256) ---");
System.out.println("キャスト前:" + g + "\tキャスト後:" + h);
System.out.println("--- 表現できるより大きな値かつ小数部含む(256.123) ---");
System.out.println("キャスト前:" + i + "\tキャスト後:" + j);
	

 

【結果】

 
--- 小数点以下切り捨て ---
キャスト前:127.567 キャスト後:127
--- 切り捨てなし ---
キャスト前:127.0 キャスト後:127
--- 表現できるより大きな値(128) ---
キャスト前:128.0 キャスト後:-128
--- 表現できるより大きな値(256) ---
キャスト前:256.0 キャスト後:0
--- 表現できるより大きな値かつ小数部含む(256.123) ---
キャスト前:256.123 キャスト後:0
	

 

まとめ

暗黙的でなく明示的な指定が必要なキャストは、値によってはキャスト前とキャスト後で値が異なることがあります

そのため明示的なキャストをする場合、切り捨てなどが行われても問題ないことが前提で使われることになります。

 

キャストによってどのような動きをするかを把握しておくことで、キャストが原因のバグにすぐ気づけるようになると思いますので、ぜひ動きの概要は把握しておいてください。