Javaに関する様々な情報をご紹介します。

15参照型の型変換とキャスト

Javaに関する様々な情報をご紹介します。

参照型の型変換とキャスト

Javaの変数には大きく分けて2つの型があります。基本型(プリミティブ型)と参照型です。基本型はJavaであらかじめ用意されているboolean、char、byte、short、int、long、float、doubleの8つのデータ型です。 参照型は、変数に値が保存されているのではなく、値が保存されている場所を参照するアドレス情報が保存されている変数のことを言います。

参照型データ

プログラムを行う際、設計時にはデータ量が分からず、実行して初めてデータ量が確定する処理があります。文字を入力したり、ファイルをアップロードしたりする処理です。その際、予め変動するデータ量を予測してメモリ内にデータ領域を確保することは難しく、また多めにデータ領域を確保するとメモリを無駄に消費することにもつながります。その対策として参照型の変数があります。

参照型の変数は、データが保存される場所を参照するアドレス情報の分のみメモリ領域を確保し、実際のデータは別の場所に保存します。アドレス情報は一定のデータ量で収まる範囲のため、メモリ領域の確保も一定の範囲に抑えることができます。Javaでは、各クラスの変数、インタフェースの変数、配列の変数が参照型の変数です。

ここでは、参照型の変数間で、異なる型の変数の代入や比較などを行う際に、Javaで行われる変換について説明します。変換には、Java実行環境により暗黙的に変換される型変換と、プログラマが意識的に変換を行うキャストがあります。
なお、基本型の変数の変換に関しては、変数(基本型の型変換とキャスト)を参照してください。

参照型の型変換

参照型の型変換は、変換元の変数と変換先の変数の関係が、変換先の変数の方がスーパークラス、スーパーインタフェース(配列の場合は配列要素がスーパークラス、スーパーインタフェース)である場合に行われます。

型変換のルール

変換元の参照型 変換先の参照型 型変換のルール
クラス型 クラス型 変換先のクラス型が変換元のクラス型のスーパークラスであること。
インタフェース型 変換先のインタフェース型のインタフェースを変換元のクラス型のクラスが実装していること。
配列型 型変換不可。
インタフェース型 クラス型 変換先のクラス型がObjectクラスであること。
インタフェース型 変換先のインタフェース型が変換元のインタフェース型のスーパーインタフェースであること。
配列型 型変換不可。
配列型 クラス型 変換先のクラス型がObjectクラスであること。
インタフェース型 変換先のインタフェース型がCloneableインタフェース、Serializableインタフェースであること。
配列型 変換元の配列要素のデータ型と、変換先の配列要素のデータ型において、型変換のルールに合致していること。

暗黙的な型変換は「代入時」と「メソッド呼び出し時」に行われます。以下では、それぞれのケースにおいて、どのように型変換が行われるのかについて説明します。

代入時の型変換

ある参照型の変数を、他の参照型の変数に代入する際に型変換が行われます。

【例1】変換元がクラス型、変換先がクラス型の例です。SuperClassクラスはSubClassクラスのスーパークラスであるとします。

SuperClass s1;
SubClass s2 = new SubClass();
s1 = s2;

変換先の変数(s1)のクラス型は、変更元の変数(s2)のクラス型のスーパークラスであるため、型変換が行われ代入可能となります。


SuperClass s1 = new SuperClass();
SubClass s2;
s2 = s1;

変換先の変数(s2)のクラス型は、変更元の変数(s1)のクラス型のスーパークラスではないため、コンパイルエラーとなります。

【例2】変換元がクラス型、変換先がインタフェース型の例です。ExClassクラスはExInterfaceインタフェースを実装しているものとします。

ExClass class1 = new ExClass();
ExInterface interface1;
interface1 = class1;

変換先の変数(interface1)のインタフェースは、変更元の変数(class1)のクラスで実装されているため、型変換が行われ代入可能となります。


ExClass class1 = new ExClass();
OtherInterface ointerface1;
ointerface1 = class1;

変換先の変数(ointerface1)のインタフェースは、変更元の変数(class1)のクラスで実装されていないため、コンパイルエラーとなります。

【例3】変換元が配列型、変換先が配列型の例です。SuperClassクラスはSubClassクラスのスーパークラスであるとします。

SuperClass[] superArray;
SubClass[] subArray = new SubClass[5];

for (int i = 0; i < subArray.length; i++) {
  subArray[i] = new SubClass();
}

superArray = subArray;

変換先の配列型の構成要素は、変更元の配列型の構成要素のスーパークラスであるため、型変換が行われ代入可能となります。

メソッド呼び出し時の型変換

メソッドを呼び出す際、呼び出すメソッドの引数の型に要求されている型と異なる型を指定した場合、暗黙的な型変換が行われます。

【例4】メソッド呼び出し時に型変換が行われる例です。

String ex1 = new String("Hello");
StringBuffer ex2 = new StringBuffer("Hello");
boolean result = ex1.equals(ex2);

equalsメソッドは引数にObject型の変数を要求します。
Objectクラスはすべてのクラスのスーパークラスであるため、StringBuffer型の変数ex2はObject型に型変換され、equalsが実行されます。


String ex1 = new String("Hello");
StringBuffer ex2 = new StringBuffer("Hello");
boolean result = ex1.equalsIgnoreCase(ex2);

equalsIgnoreCaseメソッドは引数にString型の変数を要求します。
変数ex2はStringBuffer型であり、【型変換のルール】にも合致しないため、コンパイルエラーとなります。

参照型のキャスト

キャストとはプログラマが意識的に行う変換処理です。キャストは変換したい型を( )で囲み、変換元の変数の前に指定することで行います。

(変換したい型)変換元の変数;

参照型のキャストは基本型のキャストと異なり、コンパイル時のルールと実行時のルールを意識する必要があります。それは、参照型の変数には値ではなく、値が保存されている場所を表すアドレス情報が格納されているためです。

コンパイラは参照型の変数が参照する値がどのようなものかはコンパイルする時点ではわかりません。そのため、コンパイル時にエラーが出なくても、実行する時点で初めて実行時のルールにそぐわないと判断され、エラーを返します。コンパイル時のキャストのルールを以下に記載します。【実行時のキャストのルール】は【型変換のルール】と同様です。

コンパイル時のキャストのルール

変換元の参照型 変換先の参照型 型変換のルール
クラス型 クラス型 変換元のクラスと、変換先のクラスに継承関係があること。どちらが、スーパークラスであってもかまわない。
インタフェース型 すべてキャスト可能。
配列型 参照型の配列であること。
インタフェース型 クラス型 すべてキャスト可能。
インタフェース型 すべてキャスト可能。
配列型 キャスト不可。
配列型 クラス型 変換先のクラス型がObjectクラスであること。
インタフェース型 キャスト不可。
配列型 変換元の配列要素のデータ型と、変換先の配列要素のデータ型において、キャストのルールに合致していること。

※ クラス型がfinal修飾子を実装したものである場合、上記のルールとは異なります。しかし頻繁に使用するケースではないため、ここでは割愛します。

【例5】参照型のキャストの例です。
SuperClassAはSubClassA1のスーパークラスとします。
SuperClassAはSubClassA2のスーパークラスとします。

SubClassA1 a1 = new SubClassA1();
SuperClassA a;
SubClassA2 a2;

a = (SuperClassA)a1;  //(1)コンパイルOK、実行OK
a2 = (SubClassA2)a;  //(2)コンパイルOK、実行NG

解説5

  1. (1)変数aと変数a1にはクラスの継承関係があるため、コンパイルは成功します。代入されている値は、変数aはSuperClass、変数a1はSubClassA1であり、変数aは変数a1のスーパークラスであるため、【実行時のキャストのルール】に合致し、 実行も成功します。
  2. (2)変数a2と変数aにはクラスの継承関係があるため、コンパイルは成功します。代入されている値は、変数aは①でSubClassA1が代入されているためSubClassA1、変数a2はSubClassA2であり、変数a2は変数aのスーパークラスではないため、 【実行時のキャストのルール】に合致せず、例外が返されます。

15参照型の型変換とキャスト