2016-10-28 5 views
6

言葉で説明するのは難しいですが、Java Genericsは私に予期しない結果を与えています。私は、リストがタイプ? extends Objectであると言うと、そこに何かを格納することができると思いました。したがって、タイプWrapper<? extends Object>のリストの場合は、そこに任意の種類のWrapperを格納できます。等々。それは私には意味がある。Java generics奇妙な動作

private static class Wrapper<T> { 
    public Wrapper(T t) { /**/ } 
} 

そして、私は何かしたい:これは私にエラーを与えること

private static final List<Wrapper<Wrapper<? extends Object>>> ls1 = new ArrayList<>(); 

を注:しかし、我々は持っていると仮定しましょう

public static <T> doit(T t) { 
    Wrapper<Wrapper<T>> l1 = new Wrapper<>(new Wrapper<>(t)); 
    ls1.add(l1); // nok 
    // add (Wrapper<Wrapper<? extends java.lang.Object>>) in List 
    // cannot be applied to (Wrapper<Wrapper<T>> 
} 

をしかし、私は、二次でラッパーをラップする場合ラッパー(rs)、次に:

private static class C<T> extends Wrapper<Wrapper<T>> { 
    public C(T t) { 
     super(new Wrapper<>(t)); 
    } 
} 

private static final List<C<? extends Object>> ls2 = new ArrayList<>(); 

public static <T> doit(T t) { 
    ls2.add(new C<>(t)); // ok 
} 

それは同じことであることに注意してください。これは私には意味がありません。

PS。私の場合は、ラッパーラッパーを作成するのではなく、ジェネリッククラスのThreadLocalを作成します。

+0

だからあなたが本当に欲しいものリスト '<ラッパー<ラッパー >>'のですか? – Kayaman

+0

@Kayamanはい、でもリスト>をそこに保存することはできません。私はWrapper >のどんな種類も保存できるようにしたい。 –

+0

あなたのコメントを 'String'のように具体的なものに変更できますか?これは、 'T'型の参照宣言を見るために私を捨ててしまいます。 – Zircon

答えて

2

私は基本的にあなたががラッパーにを入れたいものの上に、一般的なもの、もっと重要なのかを決定する必要がありますあなたは

このような
List<Wrapper<Wrapper<?>>> ls1 = new ArrayList<>(); 

And 

Wrapper<Wrapper<?>> l1 = new Wrapper<>(new Wrapper<>(t)); 
ls1.add(l1); // OK 
0

正直言って、私はそれがうまくいかない理由を詳しく調べなければならないでしょう。その最も一般的なタイプの消去に関連しています。 私が持っているのは回避策です、私はこのソリューションがl1オブジェクトのtypを失うことを知っています。これが大丈夫なら、それを取ってください。

Wrapper<Wrapper<? extends Object>> l1 = new Wrapper<>(new Wrapper<>(t)); 
ls1.add(l1); 
0

を行うことができます

? extends Object ---> equals ? 

を考えます、以上または何を取るラッパーの(あなたは安全に両方をすることはできません)。決定したら、extendssuperのいずれかを選択する必要があります。

ポインタを保存する場合は、List<Wrapper<Wrapper>>で十分ですが、後でキャストする必要があります。


あなたはextendsを使用している、その後? extends Fooは、任意のデータ型は、構造によってタイプFoo(またはFoo自体)のサブタイプをを返さことを意味します。例えば

あなたはvoid doIt1(List< ? extends Mammal> l ){}を持っている場合はList<Human>List<Primate>List<Simian>またはList<Mammal>に渡すことができます。

しかし、? extendsを使用している場合は、何を入れるのが安全かわかりません。

はこのことを考えてみましょう:

void addElephant(List< ? extends Mammal> l ){ 
    l.add(new Elephant()); //wrong! 
} 

これは明らかに危険なです!私はList<Human>としてlとして渡すことができました。今度はHumanの私のリストはElephantです!

一方、あなたは? super FooコンテナがFooを取ることができることを意味しsuperを持っています。 void doIt2(List< ? extends Human> l ){}ためだから、

あなたがList<Human>List<Primate>List<Simian>List<Mammal>、またはList<Object>に渡すことができますよう。

void (List< ? super Human> l ){ 
    l.add(new Human()); 
} 

これは問題ありませんが、コンテナから出るものについて何か保証することはできません。 lは本当にList<Primate>だった場合、それはHuman sおよびGorilla秒の混合物を持っている可能性があり、最初の要素がHumanになるという保証がないためである

void (List< ? super Human> l ){ 
    Human h = l.get(0); //wrong! 
} 

。このため

あなたが頭字語を取得しますP.E.C.S = "[A] P roducer ** E xtends、[しかしA] C onsumer S upers"。あなたは、コンテナは、メソッド(たとえば、get)にオブジェクトを返すしている場合、あなたは「生産」されている

(そうextendsを使用)、あなたはコンテナに要素(例えばaddを)受け入れている場合はそうでない場合、コンテナはさオブジェクトを「消費する」ので、「スーパー」を使用します。 (Nb: PECSについて覚えておくと厄介な部分は、コンテナの観点からであり、ではなく、の呼び出しコードです!)。

あなたは両方プロデュースしたいとあなたはHuman Sを追加し、Primate Sを取得することができ、具体的なタイプのリストList<Primate>を持っている必要があります消費した場合。


も参照してください:

+0

正直言って、あなたはこれに多大な努力を払っているようですが、彼は単にl1の型安全性とあなたの文章をその方法についてのエッセイにしておきたいだけです。 –

+0

@ mh-devええ、私はインターンやジュニア開発者を指すことができるようにするために、彼と彼の質問に素朴なアプローチで間違っていたことを説明したかったのです。 – ArtB