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

3ワイルドカード

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

ワイルドカード

Javaではジェネリクスでデータ型を指定する際、データ型に幅を持たせるためワイルドカードを使用することができます。ここでは、ワイルドカードの使用方法について説明します。

代入互換性

ジェネリクスのワイルドカードを理解するためには、ジェネリクスの代入互換性を理解する必要があります。Javaでは、代入する変数間にクラスの継承関係がある場合、変数の代入は可能です。しかし、ジェネリクス型の変数として使用する場合は、代入できません。

以下の代入は可能です。

String strIns = new String();
Object objIns = new Object();

//下位クラスの変数から上位クラスの変数へ代入可能
objIns = strIns;

 

しかし、ジェネリクス型の変数として使用する場合は、代入できません。

List<String> strList = new ArrayList<String>();
List<Object> objList  new ArrayList<Object>();

//コンパイルエラー
objIns = strIns;

 

もし、「objList = strList;」ができてしまうとobjListを通して、strListにString型とは異なるデータ型の値が代入され(objeList.add(100)でInteger型のデータを代入してしまう等)、想定外の実行時エラーを引き起こす原因になります。

ワイルドカード

ジェネリクスでは、ワイルドカードを使用することで使用するデータ型に幅を持たせることができます。List<Number>と指定した場合、Number型のデータしか使用できません、List<? extends Number>と指定した場合、Number型及び、Number型を継承するクラスのデータ型が使用できます。これは、指定した型と同じかその型を継承するサブクラス(サブインターフェース)であることを保証する宣言で、上限境界ワイルドカードと言います。

一方、<? super Number>と指定した場合、Number型及び、Number型のスーパークラスのデータ型が使用できます。これは、指定した型と同じかその型のスーパークラスであることを保証する宣言で、下限境界ワイルドカードと言います。

その他、上限も下限も指定しない<?>という宣言も可能です。これは、変数のデータ型に依存しない処理を行う場合に使用します。

ワイルドカードの使い方

<? extends ○○>のデータ型の変数は、その変数から値を取り出す(get)際は、上限である○○型のデータ型を返すように動作し意図したとおりに動作しますが、値を設定する(put)際は、putされる値が○○のサブクラスということは保証できるが、実際にどのクラスか特定できないため、コンパイルエラーになります。

そのため、<? extends ○○>は、変数から値を取り出す(get)のみの場合に使用します。
以下に例を記載します。

List<String> strList = new ArrayList<String>();
List<Integer> intList = new ArrayList<Integer>();

List<? extends Number> exList;
exList = strList  //コンパイルエラー
exList = intList;  //代入可能、exListとintListに継承関係があるため

Number value = exList.getValue();  //値を取得する際は、Number型として取得される
exList.setValue("test");  //コンパイルエラー
exList.setValue(10); //○○のサブクラスということは保証できるが、?が実際にどのクラスか特定できないため

 

<? super ○○>のデータ型の変数は、その変数に値を設定する(put)際は、下限である○○のデータ型を設定するように動作し意図したとおりに動作しますが、値を取り出す(get)際は、どのデータ型か特定できないためObject型として取り出されます。

Object型として取り出されるとデータ型を指定した意味がなくなるため、<? super ○○>は、変数から値を設定する(put)のみの場合に使用します。
以下に例を記載します。

List<String> strList = new ArrayList<String>();
List<Number> numList = new ArrayList<Number>();

List<? super Integer> exList;
exList = strList  //コンパイルエラー
exList = numList;  //代入可能、exListとnumListに継承関係があるため

Object value = exList.getValue();  //値を取得する際は、Object型として取得される
exList.setValue("test");  //コンパイルエラー
exList.setValue(10); //下限であるInteger型の値を設定するように動作する

 

ワイルドカードを利用することで、プログラムに柔軟性を持たせることができます。ワイルドカードが使われていない場合、同一の型同士のコレクションでしかコピーできないものが、ワイルドカードを使うことで、srcのコレクションの要素型が、dstのコレクションのサブタイプであるような場合も含めてコピーすることが可能になり、柔軟性が高くなります。

ワイルドカードを使用しない場合

public statice <E> void copy(List<E> dat, List<E> src) {
	for (int i = 0; i < src.size(); i++) {
		dst.set(i, src.get(i)); 	
	}
}

ワイルドカードを使用する場合


public statice <E> void copy(List<? super E> dat, List<? extends E> src) {
	for (int i = 0; i < src.size(); i++) {
		dst.set(i, src.get(i));
	}
}

 

<参考URL>
Java総称型のワイルドカードを上手に使いこなすための勘所

3ワイルドカード