私はジェネリックの初心者です。私の質問は次のとおりです。2つの機能の違いは何ですか。
機能1:
public static <E> void funct1 (List<E> list1) {
}
機能2:
public static void funct2(List<?> list) {
}
最初の署名は次のように述べています。list1はEのリストです。
2番目のシグニチャは次のように述べています。listはあるタイプのインスタンスのリストですが、タイプはわかりません。
メソッドを変更しようとすると違いが明らかになるため、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>
。たとえばList<T> merge(List<? extends T>, List<? extends T>)
、2つの入力リストを新しい結果リストにマージするメソッドについて考えてみます。確かに、さらに2つの型パラメーターを導入できますが、なぜ導入したいのでしょうか。それは物事を指定することを超えているでしょう。
add
メソッドを機能させることができますが、get
有用なものは何もありません。もちろん、それは次の質問を引き起こします:なぜジェネリックには下限がないのですか?より詳細な回答については、「ジェネリックメソッドを使用する場合とワイルドカードを使用する場合」を参照してください。およびhttp://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ203
ジェネリックスは、コレクションをよりタイプセーフにします。
List<E>
:ここでのEは、リストのコンテンツタイプを判別するために使用できるタイプパラメーターですが、のNo
間にコンテンツが何であったかを確認する方法がありましたruntime
。
Generics are checked only during compilation time.
<? extends String>
:これは、Typeパラメータにあった問題を処理するために、特別にjavaに組み込まれました。"? extends String"
このリストが持つことができることを意味します
objects which IS-A String.
例:
動物クラス犬クラス拡張動物虎クラス拡張動物
したがって 、コンテンツとして"public void go(ArrayList<Animal> a)"
will NOT accept
DogまたはTigerを使用しますが、Animalを使用します。
"public void go(ArrayList<? extends Animal> a)"
を作るために必要なものですArrayList take in Dog and Tiger type.
HeadFirstJavaで参照を確認してください。
パラメータタイプとしてのリストは、パラメータが任意のオブジェクトタイプのアイテムのリストでなければならないことを示しています。さらに、E
パラメーターをバインドして、関数本体内のリスト項目への参照を宣言できます。
パラメータタイプとしてのリストは、を使用する以外にリスト内のアイテムへの参照を宣言する方法がないことを除いて、同じセマンティクスを持っていますObject
。他の投稿では、さらに微妙な違いがあります。
私は通常、< E >と< ?の違いを説明します。>論理的数量化、つまり全称記号と存在記号との比較による。
したがって、次のジェネリックメソッド宣言は、すべてのクラスタイプEについて、次のように定義することを意味します。funct1
public static <E> void funct1 (List<E>; list1) {
}
次のジェネリックメソッド宣言は、< ?>、を定義しますfunct2
。
public static void funct2(List<?> list) {
}
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
はメソッドを含むクラスの名前です。)
このコンテキストでは、ワイルドカード(?)とタイプパラメーター(E)の両方が同じことを行います。ユースケースに基づいて特定のエッジがあります。次のような複数のパラメータを持つ可能性のあるメソッドが必要な場合を考えてみましょう。
public void function1(ArrayList<?> a, ArrayList<?> b){
// some process
}
public <T> void function2(ArrayList<T> a, ArrayList<T> b){
// some process
}
function1では、aは文字列のAL、bは整数のALである可能性があるため、両方のパラメーターのタイプを制御することはできませんが、これはfunction2では簡単です。 後でメソッドまたはクラスで型を使用する場合は、型パラメーター(関数2)を使用する必要があります
WildCardとTypeparamにはいくつかの機能があります。
ワイルドカード(?)
タイプParam(E)
時々、実際のタイプを渡す必要はありません。例:
ArrayList<Integer> ai = new ArrayList<Integer>();
ArrayList<Double> ad = new ArrayList<Double>();
function2(ai, ad);
//It will compile and the T will be Number.
この場合、コンパイラは実際の引数の型に基づいて型引数を推測します