ワイルドカード
ジェネリクスは非変であるため、使用するときには使う側と使われる側でどの型パラメータをやりとりするかを厳密に決めておかなければならない。
そのため、汎用的にしたい場合はワイルドカードを使用する。これにより、未知の型や特定の型の範囲を表現することができる。
ワイルドカードの主な形式
ワイルドカードには主に以下の2つの形式がある。
①非境界ワイルドカード
?
と表される。これは任意の型を表すワイルドカードである。非境界ワイルドカードは読み書き両方の操作に使用されるが、型の情報は制限される。
public void printList(List<?> list) {
for (Object element : list) {
System.out.println(element);
}
}
上記の例では、printList()
メソッドは任意の型のリストを受け取り、その要素を表示する。要素の具体的な型に依存しない処理に使用される。
特徴
主な特徴として以下の2つがある。
- メソッドの戻り値の型は
Object
型になる。 - メソッドの引数には
null
リテラルしか渡せない。
①メソッドの戻り値の型はObject型になる
非境界ワイルドカード(?
)は、任意の型を表すワイルドカードである。そのため、具体的な型の情報は制限され、戻り値の型としては最も一般的な型であるObject
型が使用される。
(全てのクラスはObject
型のサブクラスであるため、ClassCastException
が発生することがない)
例
public static void main(String[] args) {
Test<?> test = new Test<>("Hello");
String msg = (String) test.getValue();
// test.setValue("sample"); コンパイルエラー
// test.setValue(new Object()); コンパイルエラー
test.setValue(null);
}
}
もしObject
型以外の型で戻り値の参照を扱いたい場合は、上記の例のようにキャスト式を記述してダウンキャストをしなければならない。
このように、型の情報が制限されるため、具体的な操作や型の関係性によってはキャストが必要な場合がある。
また、コンパイル時の型安全性が向上するが、実行時には型の安全性が保証されないため注意が必要である。
②メソッドの引数にはnullリテラルしか渡せない
null
リテラルであれば、たとえどんな型なのかわからなくてもClassCastException
が発生することがない。
(null
リテラルは、null
型という特殊な型の唯一の値であり、どのような参照型の変数にでも代入できることがJavaの仕様で定められている)
①の特徴は、後述する上限境界ワイルドカードを使用することで改善することができるが、②の特徴の改善には下限境界ワイルドカードを使用する必要がある。
②上限境界ワイルドカード
? extends Type
と表される。Type
は特定のクラスやインターフェースを指定し、どのクラスが型パラメータとして使用できるかを制限する。
読み取り専用として使用され、特定の型の範囲を制限するために使用される。
public void processList(List<? extends Number> list) {
// list内の要素に対する処理
}
上記の例では、processList()
メソッドはNumber
クラスまたはそのサブクラスのリストを受け取ることができる。
このように、ワイルドカードに制限を設けると、Number
クラスまたはそのサブクラス型が型パラメータとして渡されてくることが保証できるため、戻り値型をObject
型ではなく任意の型にすることができるようになる。