2016-10-21 5 views
4

私はJava 8を使用しています。Java OCP 8を習得するためのトレーニング中に、わかりにくく、知りたいコードの断片が見つかりました。なぜboundsがJavaで奇妙に動作するのですか?

私は次の階層があります。

List<?> list1 = new ArrayList<A>() { 
    { 
     add(new A()); 
    } 
}; 

しかし、次のコードは動作しません、コンパイルエラー:

list1.add(new A()); 

class A {} 
class B extends A {} 
class C extends B {} 

最初 1を、このコードは仕事です

なぜ、この方法で新しいレコードを追加できないのですか?

List<? extends A> list2 = new ArrayList<A>() { 
    { 
     add(new A()); 
     add(new B()); 
     add(new C()); 
    } 
}; 

しかし、次のコードは動作しません、コンパイルエラー:

1、このコードは仕事で

list2.add(new A()); 
list2.add(new B()); 
list2.add(new C()); 

そして最後 1、このコードがあります仕事:

List<? super B> list3 = new ArrayList<A>() { 
    { 
     add(new A()); 
     add(new B()); 
     add(new C()); 
    } 
}; 

しかし、次のコードでは、我々は新しいAを追加する()、コンパイルエラー:あなたの答えのための

list3.add(new A()); // compilation error 
list3.add(new B()); 
list3.add(new C()); 

ありがとう!

+0

[PECS(プロデューサーはコンシューマースーパーを拡張するもの)とは何ですか?](http://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super) – user140547

+0

重複しない境界を使った具体的な流れです。 – badCoder

+0

@ 911DidBush、答えのおかげで、コードはmainメソッドで書かれていました。 – badCoder

答えて

3

これは型の安全性を強化するために設計されたコンパイルエラーです。コンパイラは、あなたがそれを行うには許可された場合、どうなるか想像:

問題1の場合は、オブジェクトlist1が宣言された後、コンパイラは唯一List<?>で宣言された型を、考慮され、それが最近あったという事実を無視ArrayList<A>に割り当てられています。

List<?> list1 = ...; // The compiler knows list1 is a list of a certain type 
         // but it's not specified what the type is. It could be 
         // a List<String> or List<Integer> or List<Anything> 
list1.add(new A()); // What if list1 was e.g. a List<String>? 

しかし:ここ

List<?> list1 = new ArrayList<A>() { 
    { 
     add(new A()); 
    } 
}; 

は、あなたがlist1式に代入されています。式自体、つまり=の後のすべては?を使用せず、実際にはArrayList<A>に拡張され、add(new A())を呼び出す匿名クラスはokです。

2番目の問題(list2)は同じ原因があります。第三号で

List<? super B> list3 = new ArrayList<A>() { 
    { 
     add(new A()); 
     add(new B()); 
     add(new C()); 
    } 
}; 

list3.add(new A()); // compilation error 

コンパイラはList<? super B>としてlist3を見ています。これは、汎用パラメータがBまたはそのスーパークラスAであることを意味します。 List<B>の場合はどうなりますか? AList<B>に追加することはできません。したがって、コンパイラはこのコードを拒否します。(「それは奇妙である理由」)

1

短い答えは、特定のJava短い手の表記は、彼らは本当に非常に異なっているとき、2つのコードが非常に似て見えるようにということです。

だから、「これが動作するかどうか、これも動作するはずです」のように見えることができますが、コードの2ビットで重要な違いが隠されているので、それはそうではありません。

はのは、個別にコードのいくつかの作品を見てみましょう:

public interface ListFactory { 
    List<?> getList(); 
} 

だから誰かが私たちにList<?>を要求する方法を与えるために起こっています。

List<?> list1 = myListFactory.getList(); 

をしかし、我々は

list1.add(new A()); 

をすれば、それは我々が返すListFactoryの実装を取得するために起こったかどうかに依存しているため、コンパイラは、これが合法であることを証明することはできません。私たちはこのようにそれを使用することができますList<A>、またはおそらくList<String>である。

私たちは

List<?> list1 = new SpecialList(); 
list1.add(new A()); 

で上記を交換する場合、これはあなたの元のコードに少し近いです。しかし、コンパイラに同じ問題が存在している:それはlist1.add(new A());を評価するとき、それはリスト1に割り当ての歴史の中で手がかりを探していません。これは、LIST1のコンパイル時の参照型がList<?>であることを知っているだけなので、list1.add(new A());前に違法だった場合、それはまだここに違法です。

(これはコンパイラの欠点のように感じるかもしれませんが、私はそうではないと思っています;コンパイラが参照型以上のものを試してみるのは一般的に実用的でないか望ましくありません。我々はadd(new A())が異なる参照タイプ、例えばList<A> list2 = new SpecialList();を使用できるようにオブジェクトを参照したいと思っていました。

上記は、私たちがSpecialList.javaを持っていることを意味しています。のは、それを見てみましょう:

public class SpecialList extends ArrayList<A> { 
    public SpecialList() { 
     add(new A()); 
    } 
} 

これは少し愚かに見えるかもしれませんが、それはそれで間違って何も限りコンパイラが懸念しているようがないということはおそらく何の驚きではありません(ただしAが定義されているとされるjava.util .ArrayListがインポートされます)。この場合、add(new A());this.add(new A());の省略形です

注意。 ArrayList<A>を拡張するために宣言されている - - SpecialList()コンストラクタで、thisは型SpecialListの基準であり、確かに我々ができadd(new A())ArrayList<A>のサブクラスです。ここ

List<?> list1 = new ArrayList<A>() { 
    { 
     add(new A()); 
    } 
} 
list1.add(new A()); 

線3と6なぜなら、我々は適用されてきたJavaのシンタックスシュガーで今非常によく似て:

は、だから今、私たちはあなたの元のコードのようなものを作るためにすべてのピースを持っています。しかし、3行目は本当にSpecialList()の例に似ています。つまり、add()が呼び出される参照型は、匿名サブクラスArrayList<A>です。しかし、6行目はかなり現れると思われます。そのため、最初の2つの例と同じ理由で失敗します。

類似の分析では、あなたが見ている他の奇妙な違いについて説明します。

関連する問題