回答:
最初の2つに違いはありません。タイプパラメータ(E
またはT
)に異なる名前を使用しているだけです。
第三は、有効な宣言されていない- ?
として使用されているワイルドカード型提供する際に使用される引数、例えばList<?> foo = ...
手段foo
いくつかのタイプのリストを参照し、私たちは何を知っていません。
これらはすべてジェネリックです。これはかなり大きなトピックです。もちろん、他にも利用可能なものがありますが、次のリソースを通じてそれについて学びたいと思うかもしれません。
T
とE
-彼らはただの識別子です。KeyValuePair<K, V>
たとえば、次のように書くことができます。?
ただし、特別な意味があります。
それは何よりも慣習です。
T
タイプであることを意味します E
要素であることを意味します(List<E>
:要素のリスト) K
キーです(内Map<K,V>
) V
値(戻り値またはマップされた値として) それらは完全に交換可能です(それにもかかわらず、同じ宣言での矛盾)。
これまでの回答では、型パラメーター(T、Eなど)について説明しましたが、ワイルドカード「?」、またはそれらの違いについては説明しないので、ここで説明します。
まず、明確にするために、ワイルドカードとタイプパラメータは同じではありません。型パラメーターがスコープの型を表す変数(Tなど)を定義する場合、ワイルドカードはそうではありません。ワイルドカードは、ジェネリック型に使用できる一連の許容可能な型を定義するだけです。境界(extends
またはsuper
)がない場合、ワイルドカードは「ここで任意のタイプを使用する」ことを意味します。
ワイルドカードは常に山括弧の間にあり、ジェネリック型のコンテキストでのみ意味があります。
public void foo(List<?> listOfAnyType) {...} // pass a List of any type
決して
public <?> ? bar(? someType) {...} // error. Must use type params here
または
public class MyGeneric ? { // error
public ? getFoo() { ... } // error
...
}
それらが重なり合う場所はさらに混乱します。例えば:
List<T> fooList; // A list which will be of type T, when T is chosen.
// Requires T was defined above in this scope
List<?> barList; // A list of some type, decided elsewhere. You can do
// this anywhere, no T required.
メソッド定義で可能なことには多くの重複があります。以下は、機能的には同じです。
public <T> void foo(List<T> listOfT) {...}
public void bar(List<?> listOfSomething) {...}
それで、重複がある場合、なぜどちらか一方を使用するのですか?時には、それは正直だけのスタイルです:一部の人々はあなたがいない場合と言う必要があるタイプのparamを、あなただけのより読みやすい/コードを簡単にするためにワイルドカードを使用する必要があります。上記で説明した主な違いの1つ:type paramsは、スコープ内の他の場所で使用できる型変数(Tなど)を定義します。ワイルドカードにはありません。それ以外の場合、タイプparamsとワイルドカードには2つの大きな違いがあります。
タイプparamsは複数の境界クラスを持つことができます。ワイルドカードは次のことはできません。
public class Foo <T extends Comparable<T> & Cloneable> {...}
ワイルドカードには下限を設定できます。タイプparamsは:
public void bar(List<? super Integer> list) {...}
上記では、List<? super Integer>
はInteger
ワイルドカードの下限として定義されています。つまり、リストのタイプは整数またはスーパータイプの整数でなければなりません。ジェネリック型の境界は、私が詳細にカバーしたいものを超えています。つまり、ジェネリック型をどの型にすることができるかを定義できます。これにより、ジェネリックを多態的に扱うことができます。例:
public void foo(List<? extends Number> numbers) {...}
あなたは渡すことができList<Integer>
、List<Float>
、List<Byte>
、などのためにnumbers
。型の境界がないと、これは機能しません。
最後に、ワイルドカードを使用して、他の方法では実行できないと思われる処理を行うメソッド定義を次に示します。
public static <T extends Number> void adder(T elem, List<? super Number> numberSuper) {
numberSuper.add(elem);
}
numberSuper
番号の一覧または番号(例えば、任意のスーパータイプすることができList<Object>
)、およびelem
番号または任意のサブタイプでなければなりません。すべての境界があるため、コンパイラー.add()
は型保証であると確信できます。