2012-09-23 4 views
7

なぜBar.gof2の引数を持つOKですが、引数がf1でないのはなぜですか?マルチレベルジェネリックタイプのJavaワイルドカード

public class HelloWorld { 
    public static void main(String[] args) { 
     Foo<Foo<?>> f1 = new Foo<Foo<?>>(); 
     Foo<Foo<String>> f2 = new Foo<Foo<String>>(); 
     Bar.go(f1);  // not OK 
     Bar.go(f2);  // OK 
    } 

    public static void p(Object o) { 
     System.out.println(o); 
    } 
} 

class Foo<E> { 
} 

class Bar { 
    public static <T> void go(Foo<Foo<T>> f) { 
    } 
} 

は、コンパイラが自動的に両方のケースでcapture of ?ようなタイプTを推測するべきではないでしょうか。

答えて

2

大きな質問!以下のコメントで

Foo<E>EにおけるジェネリックWRTクラス、反対としてEを使用して任意のパラメータを有することなく、Eを返すメソッドとして「共変法」、および「反変法」を定義する:一つはタイプEの仮パラメータを取りますが、Eを伴うタイプを返しません。[これらの用語の本当の定義はより複雑ですが、今のところそれを気にしない。])

これは、コンパイラがバインドしようとしているようです〜Objectの場合はf1とすると、

たとえ Foo<String> <: Foo< ? extends String >Foo< Foo<String> > <: Foo< Foo< ? extends String > >ことを意味するものではありませんのでので
class Bar0 { 
    public static <T> void go(Foo< Foo< ? extends T > > f) { 
     // can pass a Foo<T> to a contravariant method of f; 
     // can use any result r of any covariant method of f, 
     // but can't pass T to any contravariant method of r 
    } 
} 

そしてgo(f1)作品が、今go(f2)は、しません。ここで

f1f2の両方のためにコンパイルし、いくつかの変更が加えられたものです:

class Bar1 { 
    public static <T> void go(Foo< ? super Foo<T> > f) { 
     // can't properly type the results of any covariant method of f, 
     // but we can pass a Foo<T> to any contravariant method of f 
    } 
} 

class Bar2 { 
    public static <T> void go(Foo< ? extends Foo< ? extends T > > f) { 
     // can't pass a Foo<T> to a contravariant method of f; 
     // can use result r of any covariant method of f; 
     // can't pass a T to a contravariant method of r; 
     // can use result of covariant method of r 
    } 
} 
+0

作業の例を見るのは素晴らしいことです。 – Cyker

+0

'Foo >'の場合はどうですか?私が 'Foo 'という言い方で作業する必要があるなら、 'Foo <? 'と書くことができます。 extends Foo > '' Foo > 'を使うことができれば、私が実際に意図するより多くの分散をもたらすと感じます。どのように 'Foo 'を 'Foo 'として受け入れるかは、分散の観点からどのように呼び出されますか? – ony

+1

私はあなたの質問を理解していれば、それは全く受け入れられません。 'Foo >'は 'Foo < Foo< ? >>'ではありません。一般的に 'G < S >'は 'S'が 'T 'であっても' G < T >'ではありませんが 'G < S >'は 'G <? 'G'は' G < ? super S > 'です。 'Foo < ? >'は 'Foo <? extends Object> 'なので、' Foo < String > 'は' Foo < ? > 'です。しかし、 'Foo < String >'を 'S'、' Foo < ? > 'を' T'とします。上記の関係がないと、 'Foo < S >'は 'Foo < T >'ではありません。 –

2
Foo<Foo<?>> f1 = new Foo<Foo<?>>(); 

これはタイプが不明であり、任意の型のオブジェクトが不均一であり、コンパイラはFoo<Foo<?>>内のすべてのオブジェクトが同じタイプであることを保証することはできませんFoo<Foo<?>>に追加することができることを意味します。したがって、パラメータとして境界型をとるBar.goに渡すことはできません。

代わりにFoo<Foo<Object>> f1 = new Foo<Foo<Object>>();と宣言して、すべてObjectと明示的に言及するBar.goに渡すことができます。

+0

実際、Foo'が似ている 'と仮定コレクション、それは異種のものであるあなたの提案されたタイプです。 'Foo >は、指定されていない特定の同種の要素タイプを含むFoosを含むFooを意味します。 –

+0

アイデアは、 'Foo >'が無制限の型で動作し、コンパイラがそれを束縛されたものとマッチさせることができないという点を追求することでした。そして、はい、私は、用語は、 "Foo"が必要ではないコンテナであると仮定していたことに同意しますが、ポイントは残っています。 – Vikdor

+0

@JudgeMental:いいえ、 'Foo >'は異種で、 'Foo 'と 'Foo 'を同時に含むことができます。 – newacct

0

Aニース読むWhat do multi-level wildcards mean?

例:もちろん

Collection< Pair<String,Long> >  c1 = new ArrayList<Pair<String,Long>>(); 

Collection< Pair<String,Long> >  c2 = c1; // fine 
Collection< Pair<String,?> >   c3 = c1; // error 
Collection< ? extends Pair<String,?> > c4 = c1; // fine 

、我々はCollection<Pair<String,Long>>Collection<Pair<String,Long>>を割り当てることができます。ここには何も驚くべきことはありません。

Collection<Pair<String,Long>>Collection<Pair<String,?>>に割り当てることはできません。パラメータ化された型Collection<Pair<String,Long>>は、StringとLongのペアの同種のコレクションです。パラメータ化された型Collection<Pair<String,?>>は、文字列と不明な型の何らかの組の異種のコレクションです。異種のCollection<Pair<String,?>>は、例えばPair<String,Date>を含むことができ、それは明らかにCollection<Pair<String,Long>>に属しません。このため、割り当ては許可されません。

0

私は、コンパイラがBar.go(f1);を許可する場合、型システム(安全性)が壊れてしまうことを証明します:

Javaの文法を変数を宣言する型としてTgo()に使用することができます。何かのように:T t = <something>

さて、ArrayList代わりのFooを使用してみましょう、

その後、我々が持っている:

class HW { 
public static void main(String[] args) { 
     ArrayList<ArrayList<?>> f1 = new ArrayList<ArrayList<?>>(); 
     go(f1);  // not OK 
    } 

    public static <T> void go(ArrayList<ArrayList<T>> f) { 
    } 
} 

ArrayList<?>ArrayList<String>のスーパータイプである、それはあなたがmainで次の操作を行うことができることを意味し、またArrayList<Integer>のスーパータイプであります:

ArrayList<?> s = new ArrayList<String>(); 
f1.add(s); 

ArrayList<?> i = new ArrayList<Integer>(); 
f1.add(i); 

次に、コンパイラではf1を引数としてgo()を呼び出すことができます。 Tを推測するためのオプションがあります:ArrayList<Object>ArrayList<?> だからそれが許可されていないオプションと同じタイプではないので

  1. T = Objectが、ArrayList<ArrayList<Object>>ArrayList<ArrayList<?>>ではありません。

  2. T = ?、そして我々が行うことができるだろう:

    public static <T> void go(ArrayList<ArrayList<T>> f) { 
        ArrayList<T> alt1 = f.get(0); // ArrayList<String> 
        T str = alt1.get(0); 
        ArrayList<T> alt2 = f.get(1); // ArrayList<Integer> 
        alt2.add(str); // We have added String to List<Integer> 
        // ... type system broken 
    } 
    

go()あなたがしなければならない両方のケースで動作するように:

public static void go(ArrayList<? extends ArrayList<?>> f) { 
} 
+0

あなたの証拠が壊れているようです。 'alt.add(新しいInteger(1))'(私はあなたが 'add'を意味すると思います)の作業はなぜですか? 'T'と' Integer'の間のサブタイプは不明ですが、それは拒否されます。実際の証明は: 'f.get(1).add(f.get(0).get(0))'で、 'String'を' Integer'のリストに効果的に追加します。 –

+0

@Ben Schulz私の間違いを発見していただきありがとうございます。一定。 –

関連する問題