2010-12-02 8 views
10

なぜ宣言ネストされた型引数はどのように機能しますか?

Set<Set<String>> var = new HashSet<Set<String>>(); 

作業が、宣言

Set<Set<String>> var = new HashSet<HashSet<String>>(); 

チョーク?

私は、宣言のジェネリックが括弧内のルールとは異なるルールで動作する「トップレベル」(その正しいフレーズであるかどうかはわかりませんが)を知っていますが、その理由を知りたいと思います。 Googleには簡単な質問ではないので、私はあなたたちを試してみると思った。

答えて

12

許可されていれば型システムを迂回する可能性があるからです。あなたが望む特性がcovarianceと呼ばれています。コレクションが共変だったなら、あなたはこれを行うことができると思います:

Set<Set<String>> var = new HashSet<HashSet<String>>(); 

var.add(new TreeSet<String>()); 

A TreeSetのは、セットの型であり、その静的型チェックはVARにTreeSetのを挿入してからあなたを防ぐことはできませんでしょう。しかし、varは、HashSetsとHashSetsだけを想定しています。古い型のSetは期待していません。

Set<Set<String>> var = new HashSet<Set<String>>(); 

外部クラスがconrete実装を持っている必要がありますが、特にHashSetのために内部クラスを見極めるする必要は通常ありません。

個人的に、私はいつもあなたの最初の宣言を記述します。セットのHashSetを作成すると、あなたは良いことです。あなたはその後、VARにHashSetsのシリーズを挿入するように進むかどうかは、後でプログラムで選択肢が、宣言を制限する必要はありませんです。何が価値があるために


、Javaの内の配列は、コレクションクラスとは異なり、共変です。このコードはコンパイル時に捕捉されるのではなく、実行時例外をスローします。

// Arrays are covariant, assignment is permitted. 
Object[] array = new String[] {"foo", "bar"}; 

// Runtime exception, can't convert Integer to String. 
array[0] = new Integer(5); 
3

ので、ジェネリック医薬品は、Javaで動作する方法のためです。

Set<? extends Set<String>> var = new HashSet<HashSet<String>>(); 
+3

この答えは私に多くの意味がありません。 John KugelmanとColinDは本当に問題をはっきりと説明しています。 – erickson

+2

これは、型パラメータを "推論する"こととは関係ありません。 – ColinD

+0

@ColinD:正しい。一定。 – haylem

0

違いは、あなたがvarのみタイプSet<String>(うちHashSetSetである)の値を受け入れるようにできるようにしているSet<Set<String>> var = new HashSet<Set<String>>を可能にすることにより、簡単です。 Set<Set<String>> var = new HashSet<HashSet<String>>();については

varのインナータイプはSet<String>を期待するが、それはHashSet<String>を見つけたので、だけではなく、それは、コンパイルされません。これはvar.add(new TreeSet<String>());は(HashSetTreeSet間の非互換性を入力します)erronousだろうことを意味します。

これが役に立ちます。

+2

'Set <? extends Set >は、外側のセットが 'null'を除いて何も受け入れることができないことを意味しますが、' Set 'のものを含んでいます。 – ColinD

+0

そして、 'Set >'は、 '' Set 'の実装者が(' 'add()'メソッド*が許可されていると ""期待している " Setは具体的なクラスではないので、 'Set >'が 'Set 'のサブタイプを取らなかった場合、それはまったく役に立たないでしょう。 –

+0

@ColinD&Mark Peters、....私は自分の投稿を再更新しました。 –

7

理由はSet<Set<String>>Set<HashSet<String>>と等価ではないということです! Set<HashSet<String>>のみHashSet<String>を含んでいてもよいしながらSet<Set<String>>は、Set<String>任意タイプを含んでいてもよいです。

Set<Set<String>> set = new HashSet<HashSet<String>>()が合法であれば、あなたもエラーなしでこれを行うことができますが:

Set<HashSet<String>> setOfHashSets = new HashSet<HashSet<String>>(); 
Set<Set<String>> set = setOfHashSets; 
set.add(new TreeSet<String>()); 
HashSet<String> wrong = set.iterator().next(); // ERROR! 

しかし、ここでは有界ワイルドカードを使用することは合法です:

Set<? extends Set<String>> set = setOfHashSets; 

これが原因で今許可されていますセットに含まれるオブジェクトのタイプは? extends Set<String> ...つまり、「が不明です。クラスは、実装すると、Set<String>」となります。 Set<String>の特定のタイプが何であるかを正確には分からないので、それに何も追加することはできません(nullを除く)...間違っている可能性があります。最初の例。

編集:

注意あなたがあなたの質問にを参照してください。「トップレベル」のジェネリックは、1つのまたは複数の型パラメータを取るタイプを意味し、パラメータ化された型と呼ばれていること。 Set<Set<String>> set = new HashSet<Set<String>>()が合法である理由は、HashSet<T>Set<T>を実装するため、サブタイプSet<T>であるためです。ただし、型パラメータTは一致する必要があります。サブタイプがTの別のタイプSがある場合、HashSet<S>(または唯一のSet<S>偶数)はサブタイプSet<T>ではありません!上記の理由を説明しました。

これはまさにここの状況です。

Set<Set<String>> set = new HashSet<Set<String>>(); 

私たちはここにTSet<String>を交換した場合、我々はSet<T> set = new HashSet<T>()を取得します。したがって、実際の型引数が一致すること、および割り当ての右側の型が左側の型のサブタイプであることは容易に分かります。

Set<Set<String>> set = new HashSet<HashSet<String>>(); 

ここでは、STのサブタイプである場合、それぞれTSSet<String>HashSet<String>を交換する必要があります。これで、Set<T> set = new HashSet<S>()となります。上で説明したように、HashSet<S>はサブタイプSet<T>ではないため、割り当てが不正です。

0

は、最初の行は正当なJavaコードで共変アレイとコード、ある

//Array style valid an Integer is a Number 
Number[] numArr = new Integer[10] 
//Generic style invalid 
List<Number> list1 = new ArrayList<Integer>(); 
//Compiled (erased) List valid, pre generics (java 1.4) 
List list2 = new ArrayList(); 

シンプルなものにあなたの例を減らすことができます。次の行には、Lists of IntegerとNumberの無効な問題の簡単な例が含まれています。最後の行には、有効で消去された非ジェネリックリストがあります。

1.5

//this will compile but give us a nice RuntimeException 
numArr[0] = 1.5f 
//This would compile and thanks to type erasure 
//would even run without exception 
list1.add(1.5f) 

RuntimeExceptionsが有効なコードでは起こらないはずですが、1が信じてしまうようnumArrは整数のみではなく番号を保持することができます^^私には合理的な数のように思え、私たちの番号に項目を追加できます。ジェネリックスは、共分散ではないのでコンパイル時にこのエラーをキャッチします。

これらの番号と整数のリストを同じものとして受け入れることができない理由がここにあります。両方のListによって提供されるメソッドは異なる引数を受け取り、Integerリストは整数を受け付けるだけでより制限されます。これは、両方のリストによって提供されるインタフェースが互換性がないことを意味します。あなたの設定

Set<Set<String>>.add(Set<String> s) 
HashSet<HashSet<String>>.add(HashSet<String> s) 

追加し、両方のタイプの他の方法について

List<Number>.add(Number n) 
List<Integer>.add(Integer n) 

同じことが有効では互換性がありません。

(第2、私は誰かをmindreadませんでした。この時間を願って、答えにしてみてください)

関連する問題