Javaジェネリクス入門、その7
Javaジェネリクス入門、その6の続きである。JavaジェネリクスについてはJavaジェネリクスにまとめがあるので参照されたい。
前回は共変を説明した。今回は反変だ、もし完全に理解できる自信があるならば、この回は読まずにJavaジェネリクス:共変、反変、非変(これ以上簡単にはならない)を読んで欲しい。
共変のまとめ
反変の前に共変をまとめてみる。
共変オブジェクトからは値の取得のみが可能であり、書き込むことはできない。
List<? extends Animal>animalList = ...
Animal animal = animalList.get(0); // 取得は可能
animalList.add(new Cat()); // 書き込みはだめ
上の場合の「? extends Animal」という「共変」化したリストの目的としては、「Animal以下の要素を持つリストから値を取得して何かする」ためのものだった。書き込みは禁止されている。
反変とは?
共変とは逆に「取得はできないが、書き込みはできる」オブジェクトを作成するものが「反変」だが、想像の通り共変に比較すると出番はずっと少なくなる。実際にどのような場面で使うのか具体例を上げるのも難しくなる。
例えば、動物リストに犬や人間を格納することを考えてみる。普通に考えれば以下だ。
List<Animal>animalList = new ArrayList<>();
// いろいろな動物を格納する
animalList.add(new Dog());
animalList.add(new Cat());
この動物格納部分をメソッドにし、引数として格納先リストを与えたいものとする。
void addAnimals(List<Animal>animalList) {
animalList.add(new Dog());
animalList.add(new Cat());
}
この引数としては、List<Animal>しか受け入れられないのだが、しかし、AnimalはObjectでもあるで、List<Object>も使えるようにしたい。しかし、このままだとエラーになってしまう。
addAnimals(new ArrayList<Object>()); // エラー
このような時に反変として定義する。
void addAnimals(List<? super Animal>animalList) {
animalList.add(new Dog());
animalList.add(new Cat());
}
「? super Animal」は、「Animalか、その上位クラス」を意味する。Animalだけではなく、その上位のクラスでも良いということだ。これでリストの要素はAnimalでも、その上位のObjectでもOKになる。
addAnimals(new ArrayList<Object>()); // OK
addAnimals(new ArrayList<Animal>()); // OK
反変の性質
さて、反変のリストオブジェクトを引数とするメソッド内では、どのような処理が許されるかだが、基本的には書き込みのみが許され、読み込みは許されなくなる。
これらの操作制限は、その意味を考えてみれば理解できるだろう。
// リストの要素はAnimalかその上位のクラスである
void addAnimals(List<? super Animal>animalList) {
// 要素型はAnimalの可能性があるので、Animal以下しか追加できない
animalList.add(new Dog()); // これはOK
animalList.add(new Cat()); // これもOK
animalList.add(new Animal()); // これもOK
animalList.add(new Object()); // これはダメ。Animalではない。
// 要素型はObjectの可能性があるので、取得できるのはObjectとしてのみ
Animal a = animalList.get(0); // ダメ。Objectの可能性がある。
Object b = animalList.get(0); // これはOK
}
共変とは逆で、基本的に書き込みは許されるが、意図するような読み込みはできなくなる。
共変・反変の図解
図で示すと以下のようなところだ。
続きはJavaジェネリクス入門、その8になる。