WisdomSoft - for your serial experiences.

2.10 型変換

値とデータ型の関係、及び型変換の仕組みについて紹介します。また、明示的に型変換を行うキャスト変換について解説します。

2.10.1 型の拡張と縮小

複数の変数やリテラルが混ざった複雑な式を書かなければならない場合、型の互換性が問題になってきます。例えば、数値型の計算式を書くにあたって、int 型、short 型、byte 型などの異なる変数を用いる必要があるとすればどうでしょう。これらは同じ数値型に分類されますが、サイズが異なるという問題を抱えています。

このような、型の相違による問題は、型変換によって解決することができます。型変換には、暗黙的な変換と明示的な変換に分かれます。暗黙的な変換は、構文的な指示をしなくても自動的にコンパイラによって解釈されて変換され、明示的な変換は、プログラマが専用の演算子を用いて型の変換を構文的に指示する変換です。

型変換を行う場合、情報量の変化に注意しなければなりません。32 ビットの整数型を 16 ビットの整数型に変換すれば、上位 16 ビットは失われることになってしまいます。16 ビットから 32 ビットへの変換は、表現範囲が増えるだけなので失われるものはありません。前者のような、情報の破損が考えられるリスクを背負った変換をナローイング変換と呼び、情報が失われることがない変換をワイドニング変換と呼びます。Java に限らず、多くのプログラミング言語では、リスクのないワイドニング変換は暗黙的に行われるべきだと考えられています。

表1 ナローイング変換
元の型 変換後の型
byte char
short byte , char
char byte , short
int byte , short , char
long byte , short , char , int
float byte , short , char , int , long
double byte , short , char , int , long , float
表2 ワイド人具変換
元の型 変換後の型
byte short , int , long , float , double
short int , long , float , double
char int , long , float , double
int long , float , double
long float , double
float double

まったく同一の型への変換はワイドニングではなく恒等変換と呼ばれます。恒等変換は完全に冗長な変換で、明示的変換以外に意識する必要性はありません。例えば int 型から int 型への変換は恒等変換に分類されます。

2.10.2 暗黙的変換

式の値を変数に代入する時、代入変換と呼ばれる暗黙的な変換が採用されます。最終的に変数に代入される代入演算子の右オペランドは、必ず変数の型と互換性がなければなりません。そこで、代入演算子はワイドニング変換、または恒等変換を暗黙的に行います。これらの変換ができない場合、代入演算はコンパイル・エラーとなるでしょう。リスクを伴うナローイング変換は暗黙的に行われるべきではありません。

もちろん、代入変換は変数宣言文の初期化にも適用されます。

コード1
class Test {
	public static void main(String args[]) {
		byte bValue = 10;
		char chValue = 'd';
		short sValue = 1000;

		int iValue = bValue;
		System.out.println(iValue);

		iValue = chValue;
		System.out.println(iValue);

		iValue = sValue;
		System.out.println(iValue);
	}
}
実行結果
>java Test
10
100
1000

コード1では、byte 型、char 型、short 型の変数をそれぞれ iValue に代入して表示しています。型の異なる変数を代入していますが、暗黙的に代入変換が行われるため、このプログラムは問題なくコンパイルすることができます。

char 型の変数 chValue に格納している文字 'd' を int 型に変換すると 100 という値になっていることが実行結果から確認できます。英数文字が ASCII コードという定められた数値コードであることは説明しました。そのため、文字を数値に変換したり、数値を対応する文字に変換することができるのです。小文字の d は ASCII コードで10進数の 100 に相当します。

通常、代入変換でナローイング変換は行われませんが、ナローイング変換にリスクが存在しないことをコンパイル時に確定できる場合にのみ、暗黙的に行われるという特性を持ちます。リスクがないナローイング変換とは、すなわち数値型リテラルを用いており、そのリテラルが変換後の型で表現可能な範囲である場合です。10 や 1000 という整数リテラルは int 型であると解釈されます。

しかし、コード1では整数リテラルで byte 型や short 型の変数を初期化しています。これは明らかにナローイング変換ですが、コンパイル時に情報が失われないことが確定されているため、暗黙的な変換を許可するのです。bValue に代入する値が 128 以上であれば、情報が失われる可能性があるため、コンパイル・エラーとなります。

もう 1 つの暗黙的な変換は、数値の格上げです。数値の格上げとは、算術演算のオペランドに対して行われるワイドニング変換、または恒等変換のことです。算術式に用いられるオペランドの型を一致させなければ計算を行うことができません。しかし、算術演算に byte 型と int 型といった、異なるサイズの数値型オペランドが渡されることは十分予想できるものです。そこで、暗黙的に数値の格上げを行い、型を統一してから計算を行うのです。

単項演算子の場合、オペランドの型が byte、short、char のいずれかであれば int 型に変換されます。それ以外の場合は、そのまま処理されます。2 項演算子であれば、最もサイズの大きい整数型に合わせて格上げされます。例えば、いずれかのオペランドに double 型があれば、もう一方を double 型に変換するという形になります。

コード2
class Test {
	public static void main(String args[]) {
		byte bValue = 10;
		char chValue = 'd';
		short sValue = 1000;

		int iValue = bValue + chValue + sValue;
		System.out.println(iValue);
	}
}
実行結果
>java Test
1110

コード2では、byte 型、char 型、short 型の変数をそれぞれ加算しています。このとき、各変数は型が異なるため、計算を行うときに数値の格上げが行われています。そのため、このプログラムは問題なくコンパイルすることができます。

2.10.3 キャスト変換

暗黙的な変換に対して、情報を削るナローイング変換を行うには明示的な変換を行わなければなりません。明示的な変換とは、キャスト演算子を用いたキャスト変換のことです。キャスト変換は、恒等変換、ワイドニング変換、ナローイング変換のすべてを行うことが可能であり、通常は暗黙的に変換することができない変換処理に使います。

キャスト演算子(しばしばキャスト式とも表現される)とは、括弧の中に変換する型を指定した単項演算子です。オペランドの値は、キャスト変換によって指定した型に実行時変換されます。

キャスト演算子
(型名) オペランド

ただし、ありとあらゆるものが変換可能というわけではありません。文字列を数値に変換できないことなどは、感覚的にも理解できるでしょう。オブジェクト指向の世界では、犬オブジェクトを哺乳類オブジェクトに変換することは可能ですが、猫に変換することはできないのです。生物学的に、犬は哺乳類に分類されますが、犬は猫ではないことと同じです。

整数をナローイング変換した場合、カットされた上位ビットの情報は失われてしまいます。例えば、int 型の整数 0xABCD を byte 型に変換すると、上位8ビットの情報は失われて 0xCD という値が返されます。ナローイング変換する場合、開発者は失われる情報に注意しなければなりません。

コード3
class Test {
	public static void main(String args[]) {
		int iValue = 0x00420041;

		String str = "(char)iValue = " + (char)iValue + '\n' +
			"(char)(iValue >> 16) = " + (char)(iValue >> 16);
		System.out.println(str);
	}
}
実行結果
>java Test
(char)iValue = A
(char)(iValue >> 16) = B

コード3は int 型の 32 ビット整数を、キャスト演算子を使って2つの文字に分解しています。iValue には、上位 16 ビットに ASCII コードの B、下位 16 ビットに A をあらわす値が格納されています。これを、char 型に変換することで表示可能な文字として認識させています。