本日のお裾分け

日々の開発で得た知識をシェアします。Java/Scala/Ruby/javascript

【めも】Java資格取得(OCJP Gold)のためのジェネリクスまとめ...になる予定のもの

ジェネリクスと、オールマイティ型(上限型・下限型指定あり)について学習中のめもです。

Listは型パラメータをもつので、ジェネリクスで格納する値を指定できます。

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

代入をする場合は、宣言している型と同じであることを<>で表現できます。<>がダイヤモンドみたいなんでダイヤモンド演算子と呼ぶらしいですw

List<Integer> intList = new ArrayList<>();//new ArrayList<Integer>();と同じ。  
strList = new ArrayList<>();//strListは上でList<String>で宣言されているので、new ArrayList<String>();と同じ。  

ジェネリクスの指定をすると、指定した型以外の代入やaddはできなくなります。

strList = new ArrayList<Integer>();//コンパイルエラー  
strList.add(1);//コンパイルエラー  

?はオールマイティ型と呼ばれ、型パラメータとして指定することでどんな型でも代入できるようになります。

List<?> list;  
list = new ArrayList<String>();  
list = new ArrayList<Integer>();  

しかし、オールマイティ型を利用するとaddはできなくなります。

list.add("1");//コンパイルエラー  
list.add(1);//コンパイルエラー  
list.add(new Object());//コンパイルエラー  

また、オールマイティ型を利用するとgetの戻り値はObjectになります。

Object value = list.get(0);  
Integer i = list.get(0);//コンパイルエラー  

ここで、こんなクラスがいたとします。

class Parent {}  
class Child extends Parent {}  
class GrandChild extends Child {}  

オールマイティ型には上限型・下限型を指定できます。
上限型を指定する場合は、? extends クラス名と書きます。
以下は?はParentが先祖ですよという意味。

List<? extends Parent> up2ParentList;  
up2ParentList = new ArrayList<Parent>();  
up2ParentList = new ArrayList<Child>();  
up2ParentList = new ArrayList<>();//ダイヤモンド演算子も指定できます。  
up2ParentList = new ArrayList<String>();//コンパイルエラー。Stringは先祖にParentがいないので。  

しかし、オールマイティ型の指定なのでaddは相変わらずできません。

up2ParentList.add(new Child());//コンパイルエラー。ChildはParentを継承していますがaddできません。  
up2ParentList.add(new Parent());//コンパイルエラー。Parent自身もaddできません。  

?のみ指定の場合と異なるのは、戻り値の型がObjectではなく上限型に指定した値となることです。

Parent p = up2ParentList.get(0);  

下限型を指定する場合は、? super クラス名と書きます。
以下は、?はChildの先祖ですよという意味。

List<? super Child> down2ChildList;  
down2ChildList = new ArrayList<Parent>();  
down2ChildList = new ArrayList<Child>();  
down2ChildList = new ArrayList<GrandChild>();//コンパイルエラー。GrandChildはChildに継承されていないので。  
down2ChildList = new ArrayList<>();//ダイヤモンド演算子も指定できます。  
down2ChildList = new ArrayList<String>();//コンパイルエラー。StringはChildの先祖でないので。  

上限型と異なる点はaddができる点です。ただし、下限型としてaddされているのか、下限型の親クラスは代入できないようです。。。

down2ChildList.add(new Parent());//ArrayList<Parent>()を代入できるのに、add(new Parent())できないのはわかりづらい。  
down2ChildList.add(new Child());  
down2ChildList.add(new GrandChild());  

しかし、上限型では上限型で戻り値が取得できましたが、下限型では?のみと同様にObjectしか返せません。

Object o = down2ChildList.get(0);  
Child c = down2ChildList.get(0);//コンパイルエラー。  

以上。
ジェネリクスは動きから理解するのが難しいから、
なぜこの仕様なのか?については別記事にてまとめます。

参考

Java総称型のワイルドカードを上手に使いこなすための勘所 http://d.hatena.ne.jp/ryoasai/20110325/1301078699