SetとSetの違いまとめ
昨日の続き。「Set<String>」,「Set<?>」,「Set<? extends String>」,「Set<? super String>」の挙動について。
要素の追加
「set.add(object);」で縦軸の型のオブジェクトを追加可能かどうか。
Set<String> | Set<?> | Set<? extends String> | Set<? super String> | ||||||
---|---|---|---|---|---|---|---|---|---|
Object | × | × | × | × | |||||
String | ○ | × | × | ○ |
- Object型のオブジェクトはどれにも追加できない。
- 「Set<String>」,「Set<? sueper String>」はString型のオブジェクトを追加できる。
- 「Set<? sueper String>」は「Stringの上位クラスの何かを受けつけるが詳細は不明」であることを示す。つまり、Set<String>やSet<Object>が該当する。これらは少なくともStirngを持ちうるのでString型のオブジェクトを追加できる。
- 「Set<?>」,「Set<? extends String>」は何も追加できない。
- 「Set<?>」は「受け付ける型が不明」を示すので追加できない。
- 「Set<? extends String>」は「String派生の何かを受けつけるが詳細は不明」であることを示す。つまり、Set<String>やSet<Stringの派生クラス>が該当する。(Stringの派生は実際にはありえないが。)ここで、Set<? extends String>の実体がSet<Stringの派生クラス>だった場合、Stringが挿入できるのはまずい。よって「Set<? extends String>」にはオブジェクトを追加できない。
Set<String派生クラス> set = null; Set<? extends String> set2 = set; // これは可能。 set2.add( "hoge" ); // Set<String派生クラス>にStringが追加されてしまう!! のでコンパイルエラーになる。
要素の取得
「set.iterator().next();」で取得できる要素が縦軸の型の変数にキャストなしで代入可能かどうか。
Set<String> | Set<?> | Set<? extends String> | Set<? super String> | ||||||
---|---|---|---|---|---|---|---|---|---|
Object | ○ | ○ | ○ | ○ | |||||
String | ○ | × | ○ | × |
- 「Set<String>」は中身がStringなのでStringで取得できる。
- 「Set<? extends String>」は中身が少なくともStringではあるのでStringで取得できる。
- 「Set<?>」の要素は「不明」なので、戻り値はObject型になる。
- 「Set<? super String>」の要素は「Stringの上位クラスの何か」なのでObject型でしか取得できない。
代入
「setA = setB;」のように、を横軸の型のSetBを縦軸の型のSetAに代入できるか。
A↓B→ | Set<String> | Set<?> | Set<? extends String> | Set<? super String> | |||||
---|---|---|---|---|---|---|---|---|---|
Set<String> | - | × | × | × | |||||
Set<?> | ○ | - | ○ | ○ | |||||
Set<? extends String> | ○ | × | - | × | |||||
Set<? super String> | ○ | × | × | - |
- 「Set<String>」は他のすべてに代入できるが逆は不可。
- Set<? extends String>はSet<String派生クラス>の場合がある。
- Set<? super String>はSet<Object>の場合がある。
- 「Set<?>」は他のすべてを代入できるが逆は不可。
- 「Set<? extends String>」,「Set<? super String>」には「Set<String>」を代入できる。どちらも「Set<String>」をとりうるため。
Setごと追加
「setA.addAll(setB);」のように、横軸の型のSetBを縦軸の型のSetAにまとめて追加できるか。
A↓B→ | Set<String> | Set<?> | Set<? extends String> | Set<? super String> | |||||
---|---|---|---|---|---|---|---|---|---|
Set<String> | - | × | ○ | × | |||||
Set<?> | × | - | × | × | |||||
Set<? extends String> | × | × | - | × | |||||
Set<? super String> | ○ | × | ○ | - |
- 「Set<? extends String>」は、「Set<String>」のaddAll()に指定できる。
- 「Set<? super String>」のaddAll()には、少なくとも要素にStirngを持つSetは指定できるので、「Set<String>」「Set<? extends String>」はOK
まとめ
- 「Set<String>」
- Stringとその派生クラスを持つSet。
- 「Set<?>」
- なにを持つか不明なSet。要素はStringかも知れないし、Objectかもしれないし、Integerかも知れない。
- なにを持つか不明なので、何も追加できない && 値はObjectとしてしか取り出せない。
- 「Set<String>」や「Set<Object>」を抽象化して透過的に扱いたい時に使う。
- 「Set<? extends String>」
- String派生の何かを要素として持つが詳細は不明なSet。Set<String>やSet<Stringの派生クラス>が該当する。
- 「Set<String>」や「Set<Stringの派生クラス>」を抽象化して透過的に扱いたい時に使う。要素は少なくともStringなので、値をString型で取り出せるのがメリット(「Set<?>」はObjectでしか取り出せない。)逆に、実際に受け付けることができる型は不明であるため、要素の追加はできない。
- 「Set<? super String>」
- Stringより上位の何かを要素として持つが詳細は不明なSet。Set<String>やSet<Object>が該当する。
- 「Set<String>」や「Set<Object>」を抽象化して透過的に扱いたい時に使う。要素は少なくともStringより上位なので、String派生のオブジェクトを追加できるのがメリット。ただし、値はObjectでしか取り出せない。
なるほど。ちょっとわかってきたぞ。
補足:検証で使用したコード
検証で使用したコードは以下。コメントアウトされているところはコンパイルエラーになります。
Set<String> set1 = new HashSet<String>(); Set<?> set2 = new HashSet<Object>(); Set<? extends String> set3 = new HashSet<String>(); Set<? super String> set4 = new HashSet<Object>(); set1.add( "" ); //set1.add( new Object() ); //set2.add( "" ); //set2.add( new Object() ); //set3.add( "" ); //set3.add( new Object() ); set4.add( "" ); //set4.add( new Object() ); String s; Object o; o = set1.iterator().next(); s = set1.iterator().next(); o = set2.iterator().next(); //s = set2.iterator().next(); o = set3.iterator().next(); s = set3.iterator().next(); o = set4.iterator().next(); //s = set4.iterator().next(); // 代入。 //set1 = set2 //set1 = set3 //set1 = set4; set2 = set1; set2 = set3; set2 = set4; set3 = set1; //set3 = set2; //set3 = set4; set4 = set1; //set4 = set2; //set4 = set3; // //set1.addAll( set2 ); set1.addAll( set3 ); //set1.addAll( set4 ); //set2.addAll( set1 ); //set2.addAll( set3 ); //set2.addAll( set4 ); //set3.addAll( set3 ); //set3.addAll( set2 ); //set3.addAll( set4 ); set4.addAll( set1 ); //set4.addAll( set2 ); set4.addAll( set3 );