2016-05-23 17 views
0

私はC++や他の言語で多くの経験を持っていますが、私はJavaでかなり新しいです。テンプレート/ジェネリックは私が知らないものではありません。Javaでジェネリックを使用する場合、なぜ<?>を指定する必要がありますか?

ありしかし私を気に何かがだ、それは私が、私はそれがされる特定のタイプの事前に知っていないとき、私は私が何かの一般的なインスタンスを使用するたびに使用する必要があると言われたというこの<?>です:

同様:

List<MyGeneric> foo; // bad 
List< MyGeneric<?> > bar; // good 

最初の式を使用した場合のIntelliJは私に嘔吐しない、そしてなぜそれが必要私は理解していません。私の同僚は、2番目の表現がであり、それ以上にはであると表現していますが、その理由を正確には伝えられませんでした。

私たちが操作するジェネリックであるという事実について明示されている点を除き、これらの2つの違いはどういう意味ですか?

コンパイラは確かにが、それはコンパイル時にジェネリックであることを知っているが、そうの推測では、それは彼がジェネリックを操作しているプログラマに伝えるため、2番目の式は唯一優れていることです。

私は正しいですか?

編集:私は、私はそこに格納するつもりです何を事前に知っていたときには明確化のために、私はovbiously、List<MyGeneric<Double>>のように、最も制限のタイプを使用しています。私の質問は、未知の種類のジェネリックを保存するときです。

+2

"私は何かの一般的な例を使うたびに私は使うべきだと言われました。"メッセンジャーを撃つ!そして... "私の同僚たちは、2番目の表現がはるかに優れていると表明しましたが、正確になぜそれを伝えることはできませんでした。自分の議論を裏付けることができない人々と話している。議論する – ChiefTwoPencils

+0

最初のものが悪い理由の単純な例: 'for(MyGeneric obj:foo){}'を使ってリストを反復し、あなたは生の 'MyGeneric'インスタンスを持っています。 –

+0

それはあなたがnullではないすべての型を渡すことができます –

答えて

3

違いは生のタイプは、がクラスがジェネリックであることを無視し、ワイルドカード<?>はクラスがジェネリックであるが型引数が不明であることを指定します。

生の意味は、すべてのコンパイラのタイプチェックが失われることを意味します。ワイルドカードは、型チェックをそのまま維持します。

例:

public class MyGeneric<T> { 
    private T val; 
    public T get() { 
     return this.val; 
    } 
    public void set(T val) { 
     this.val = val; 
    } 
} 

MyGeneric a = new MyGeneric<Integer>(); 
a.set("Foo"); // accepted 

aのことを意味し、生定義されたので、それはコンパイラによって受け入れられるIntegerあると宣言されたStringaの値を設定しますコンパイラはクラスがジェネリックであるという事実を無視しています。 valを後でIntegerとして使用すると、プログラムがクラッシュします。それは外出するのを待っている爆弾です。コンパイルされませんbの値を設定しようと


MyGeneric<?> b = new MyGeneric<Integer>(); 
b.set("Bar"); // compile error 

The method set(capture#1-of ?) in the type MyGeneric<capture#1-of ?> is not applicable for the arguments (String) 
ここ

コンパイラはクラスがジェネリックであることを知っているとさえ(何に値を設定することができません。 Integer)、どのタイプが許可されるのかわからないため(ワイルドカード=不明、覚えていますか?)コンパイラはここで必要なように、ここで保護します。

+0

こちら誠の答えは生ポインタの説明にリンクしていますが、これは重要な理由を説明する答えです。ありがとう、アンドレアス。 – Gui13

9

すべて時間?それは常に適用可能ではなく、必ずしも意味をなさない。

のは、それが実際に何であるかを説明してみましょう:<?>はすぐに二つのことを意味しunbound wildcard、次のとおりです。

  • MyGenericは、一般的なクラスですが、
  • あなたは(それが保持してどのような種類を知らないとおそらく問題ではない)。

それはあなたがraw typeと仕事ができるだろう、とあなたが本当に生の型を使用しないことを最初の式点で、第1の式が常に保証へ望ましいです。しかし、毎回、ワンディングされていないワイルドカードを使用するのが理想的であると仮定すると、過大な総体化となります。

実際にを知っているか、型を気にしたり、その境界を知っていたり気にしたりしている場合は、その代わりに使用してください。

+0

私の質問は明らかに、私は確かに(私は私がダブルスを格納することを知って 'List >'のような)可能な限り*特定のジェネリック型を使用します。私の質問は、私が複数のジェネリックをいつ保存するかについてです。私は言い換えようとします! – Gui13

+3

@ Gui13:私の答えはまだ適用されます。ジェネリックが保持しているタイプを知らない、または気にしない場合は、ワイルドカードを使用しても問題ありません。しかし、たとえば、そのコレクションを反復して、ジェネリック型に文脈依存の各エントリに対して操作を実行するような場合は、最も制限の厳しい型を使用することをお勧めします。 – Makoto

+0

@ Gui13具体的にはなぜ 'MyGeneric 'を 'MyGeneric'の代わりに使用するのですか?これは' MyGeneric'は生の型なので、Makotoの答えにあるリンクを見てください:[生の型とは何ですか?それ?](http://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it) – Jesper

4

最初に使用するのが悪い理由の例を挙げてみましょう。:あなたは生タイプを使用しているので、これはコンパイル

List<MyGeneric> list = new ArrayList<>(); 
list.add(new MyGeneric<>("Hello")); 

for (MyGeneric instance : list) { 
    Integer value = (Integer) instance.get(); // Compiles, but fails at runtime. 
} 

class MyGeneric<T> { 
    private final T instance; 
    MyGeneric(T instance) { this.instance = instance; } 
    T get() { return instance; } 
} 

次のコードはコンパイルして実行されますが、ClassCastExceptionと、実行時に失敗していました:MyGenericは、次のように定義されていると仮定すると、コンパイラはinstance.get()Integerを返すことができないことを知らない。単に安全ではないと警告するだけです。

一方、次のコードはコンパイルさえないでしょう:

List<MyGeneric<String>> list = new ArrayList<>(); 
list.add(new MyGeneric<>("Hello")); 

for (MyGeneric<String> instance : list) { 
    Integer value = (Integer) instance.get(); // Won't compile, incompatible types. 
} 
+0

@アンドレアスの感謝、固定。 –

0

List<?>は、未知の型に型指定されたリストを意味します。これは、あなたがListがに入力される入力のか分からないので、あなただけのコレクションから読み取ることができ、そしてあなただけObjectとして読み込まれたオブジェクトを扱うことができList<A>List<B>List<String>など

可能性がありインスタンス。次に例を示します。

public void processElements(List<?> elements) { 
    for(Object o : elements){ 
     System.out.println(o); 
    } 
} 

​​方法は、現在のパラメータとして任意の汎用Listで呼び出すことができます。例えばList<A>のために、ここでList<B>List<C>List<String>などが有効な例です:チュートリアル後

List<A> listA = new ArrayList<A>(); 

processElements(listA); 

はさらに、あなたがそれを理解するのに役立ちます:

+1

@dur提案していただきありがとうございます。私は今説明を追加しました。 –

関連する問題