私はジェネリックの初心者ですが、私の質問は次のとおりです。2つの関数の違いは何ですか。
機能1:
public static <E> void funct1 (List<E> list1) {
}
機能2:
public static void funct2(List<?> list) {
}
最初の署名は言う:list1はEのリストです。
2番目のシグニチャは言う:リストはあるタイプのインスタンスのリストですが、タイプはわかりません。
メソッドを変更しようとすると、違いが明らかになり、2番目の引数が必要になります。これは、メソッド内のリストに追加する必要があります。
_import Java.util.List;
public class Experiment {
public static <E> void funct1(final List<E> list1, final E something) {
list1.add(something);
}
public static void funct2(final List<?> list, final Object something) {
list.add(something); // does not compile
}
}
_
最初のものはうまく動作します。また、2番目の引数を実際にコンパイルされるものに変更することはできません。
実際、私は違いのさらに優れたデモンストレーションを見つけました:
_public class Experiment {
public static <E> void funct1(final List<E> list) {
list.add(list.get(0));
}
public static void funct2(final List<?> list) {
list.add(list.get(0)); // !!!!!!!!!!!!!! won't compile !!!!!!!!!
}
}
_
(@Babu_Reddy_Hがコメントで行ったように)何ができるかを制限するだけで、なぜ_<?>
_が必要なのでしょうか。ワイルドカードバージョンには次の利点があります。
呼び出し元は、渡したオブジェクトについてあまり知る必要がありません。たとえば、リストのマップがある場合:_Map<String, List<?>>
_リストの要素のタイプを指定せずに、その値を関数に渡すことができます。そう
このようにパラメーター化されたオブジェクトを配布する場合、これらのオブジェクトについて人々が知っていることと、オブジェクトがそれを使って何ができるか(彼らが安全でないキャストから離れている限り)を積極的に制限します。
これら2つを組み合わせると、_List<? extends T>
_になります。たとえば、2つの入力リストを新しい結果リストにマージするメソッドList<T> merge(List<? extends T>, List<? extends T>)
について考えてみます。確かに、さらに2つの型パラメーターを導入できますが、なぜそうしたいのですか?それは物事を特定することよりも多いでしょう。
add
メソッドを機能させることができますが、get
は有用なものを提供しません。もちろん、それが次の質問のきっかけになります。ジェネリックに下限がないのはなぜですか。より詳細な回答については、 ジェネリックメソッドを使用する場合とワイルドカードを使用する場合 および http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.htmlを参照してください。 #FAQ2
ジェネリックスは、コレクションをよりタイプセーフにします。
List<E>
:Eはタイプパラメータです。これはリストのコンテンツタイプを決定するために使用できますが、No
中にコンテンツが何であるかを確認するruntime
方法がありました。
Generics are checked only during compilation time.
<? extends String>
:これは、Javaに特別に組み込まれており、タイプパラメータに関する問題を処理していました。 "? extends String"
は、このリストが持つことができることを意味します
objects which IS-A String.
たとえば:
動物クラス犬クラスは動物を拡張するタイガークラスは動物を拡張する
したがって、"public void go(ArrayList<Animal> a)"
を使用すると、コンテンツとして動物または動物としてNOT accept
DogまたはTigerが使用されます。
"public void go(ArrayList<? extends Animal> a)"
は、ArrayList take in Dog and Tiger type.
を作成するために必要なものです。
Head First Javaでリファレンスを確認してください。
私は通常、<[〜#〜] e [〜#〜]>と<?>の違いを論理的な数量化、つまりユニバーサル数量化との比較によって説明しています。存在定量化。
したがって、次のジェネリックメソッド宣言は、すべてのクラスタイプ[〜#〜] e [〜#〜]に対して、funct1
を定義することを意味します
public static <E> void funct1 (List<E>; list1) {
}
次のジェネリックメソッド宣言は、<?>で示されるいくつかの既存のクラスに対して、funct2
を定義することを意味します。
public static void funct2(List<?> list) {
}
パラメータタイプとしてのリストは、パラメータが任意のオブジェクトタイプのアイテムのリストでなければならないことを示しています。さらに、E
パラメーターをバインドして、関数本体内のリスト項目への参照を宣言できます。
パラメータ型としてのリストは、Object
を使用する以外にリスト内の項目への参照を宣言する方法がないことを除いて、同じセマンティクスを持っています。他の投稿はさらに微妙な違いを与えます。
1つ目は、Eタイプの項目のリストでなければならないパラメーターを受け入れる関数です。
2番目の例の型は定義されていません
List<?> list
そのため、任意のタイプのオブジェクトのリストを渡すことができます。
(編集後)これらの2つの関数シグネチャは、外部コードに対して同じ効果があります-どちらもList
を引数として受け取ります。ワイルドカードは、1回だけ使用される型パラメーターと同等です。
前述の違いに加えて、次のような違いもあります。ジェネリックメソッドの呼び出しに対して型引数を明示的に設定できます。
List<Apple> apples = ...
ClassName.<Banana>funct2(apples); // for some reason the compiler seems to be ok
// with type parameters, even though the method has none
ClassName.<Banana>funct1(apples); // compiler error: incompatible types: List<Apple>
// cannot be converted to List<Banana>
(ClassName
は、メソッドを含むクラスの名前です。)