目次へ戻る 下へ↓
8、キャストの概念
作成者:Fumitaka Makino 更新日:2003-04-04 13:49

1,キャストとは?

前回の継承の概念に続いて今回も大事な考え方です。ただ継承の考え方を補強するものであると思っていただければよいです。早速ですが、キャストとは何かと言いますと、「型の再定義」のことです。簡単な例でいうと下記のようにint型の変数をlong型の変数に代入するなどです。

例:int型からlong型へのキャスト

   
 

int i = 1;
long l1 = (long)i;
long l2 = i;//この場合はキャスト演算子を付加しなくても良い

 
   

int型の変数 i をlong型に代入するときに「(long)」という表記が見られると思います。これはキャスト演算子と呼ばれ変換する目的の型を括弧の中に記述します。値の代入時のキャストの方法は一般的に以下のように記述します。

もし無理な型変換を行った際にはコンパイルエラーが出ます。ではキャスト可能な状態にはどのようなものがあるのでしょう。キャストの種類にはやはりプリミティブ型とクラス型の違いがありますが、まずはプリミティブ型について説明しましょう。プリミティブ型には下記のように8種類あります、このうち相互にキャスト可能なものはbooleanを除く7種類です。

プリミティブ型
値の範囲
byte 1バイト符号無し整数(0〜255)
short 2バイト整数(-32768〜32767)
int 4バイト整数(-2147483648〜2147483647)
long 8バイト整数(-9223372036854775808〜9223372036854775807)
float 4バイト単精度浮動小数点数
double 8バイト単精度浮動小数点数
char 2バイト文字データ(\u0000〜\uffff)
boolean trueまたはfalse

ただいくつか留意しなければいけない点があります。

  • 浮動小数点と整数間の変換は小数点以下が切り捨てられる。
  • 表現力の大きな型から小さな型への変換時に、キャストする値が小さな型の表現力を超えている場合は桁が切り捨てられるうえ、符号部分がおかしくなります。(例:longからintなど)

これらを考慮した上で値を変換すれば、プリミティブ型での値の変換は問題がないと思われます。例えばStream系のクラスを用いるとbyte型をint型で扱ったりすることもありますので、相互変換を忘れないようにしてください。

ではクラス型での型変換にはどのような決まりがあるのでしょう。クラス型においてはキャストできる型の種類と言うのは以下のものと決まっています。

  • 基本的に全てのクラスはObject型として扱える。
  • 自分と自分のスーパークラスの型として扱える。
  • 自分と自分のスーパークラスのインプリメントしているインターフェースの型として扱える。(もしわからなければ、インターフェースの節で詳しく説明するのでとりあえずおいておいてください。)

これは前節で学習した「型の遺伝」に基づくものです。簡単に言ってしまうと

「自分が継承しているクラス型として扱える」

と言うことです。クラス型においてもキャスト演算子の利用法はかわりません。しかしプリミティブ型のキャストように値を変更したりするようなことはおきません。型を変更するのみで実体に対しては何も行いません。つまり下記のようにStringクラスのインスタンスをObject型として扱った場合も、実体はやはり最初のStringクラスのインスタンスであると言うことです。

例:StringクラスのObject型へのキャスト

   
 

String str = "てすと";

//継承によりObjectクラスの機能を持っているからObject型として扱えるだけであり、
// 実体は「"てすと"」 というStringクラスのインスタンスである。
Object ob = (Object)str;

 
   

ではキャストできない場合を考えてみましょう。プリミティブ型の場合はコンパイルエラーとなりますが(booleanをintでキャスト)、クラス型の場合にはコンパイルエラーになる場合と、実行時エラーとして扱われる場合があります。コンパイルエラーのほうから考えていきましょう。

例:クラスキャスト時のコンパイルエラー

   
 

String str = "てすと";
Integer integer = (Integer)str;//StringのインスタンスをIntegerでキャスト

 
   

上記の場合、明らかにコンパイルエラーとなります。なぜならStringクラスのスーパークラスにはIntegerクラスが存在しないからです。つまりStringクラスはIntegerクラスを継承していないと言うことです。そのためコンパラーがエラーを出します。では実行時エラーの場合を見てみます。

例:クラスキャスト時の実行時エラー

   
 

String str = "てすと";
Object ob = str;//これはダウンキャストと呼ばれ親方向にキャストする場合にはキャスト演算子が必要ありません。
Integer integer = (Integer)ob;//Object型で扱われているStringのインスタンスをIntegerでキャスト

 
   

あまりさっきと変化がないように見受けられますね。2行目ではString型のインスタンスをObject型としてダウンキャスト(拡張するまえの型として扱うから)しています。そして3行目ではObject型であるobをInteger型でキャストしています。先ほどのコンパイルエラーのコードを見たあとだとやはりこれもコンパイルできないように見えます。ところがこれはコンパイルできます。

なぜかというと、コンパイラは以下のように考えるからです。

  1. 変数strのクラス型であるStringクラスは親クラスにObjectクラスを持つからObject型として扱っても問題ない、変数obに代入しよう。
  2. IntegerクラスはObjectクラスを継承しているので変数obはひょっとしたらIntegerクラスのインスタンスをObject型へとダウンキャストしたものかもしれない(つまりobの実体はIntegerクラスのインスタンスかもしれない)。
  3. だから一応キャストしておこう。

そのためにコンパイルは問題がないのです。ところが当然ですが変数obの中身はStringクラスのインスタンスなので、Integer型としてはキャストできません。そのため実行時にClassCastExceptionが発生しプログラムは終了してしまいます。

VectorやHashtableなどのオブジェクトを扱うクラスなどでは戻り値がObject型でリターンされてくるのでキャストが必須になります。よくわからない場合は落ち着いて何度か読み返してください。

 

2,キャストの実験プログラム

正常なキャスト、ダウンキャストを行う実験プログラム(CastTestMain1.java

キャストエラーであるClassCastExceptionが発生する実験プログラム(CastTestMain2.java

それぞれJavaDocはこちら

↑上へ 目次へ戻る