53

私はジェネリックの初心者です。私の質問は次のとおりです。2つの機能の違いは何ですか。

機能1:

public static <E> void funct1  (List<E> list1) {

}

機能2:

public static void funct2(List<?> list) {

}
4

8 に答える 8

37

最初の署名は次のように述べています。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

于 2012-06-08T05:07:21.500 に答える
7

ジェネリックスは、コレクションをよりタイプセーフにします。

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 acceptDogまたはTigerを使用しますが、Animalを使用します。

"public void go(ArrayList<? extends Animal> a)"を作るために必要なものですArrayList take in Dog and Tiger type.

HeadFirstJavaで参照を確認してください。

于 2012-06-08T05:03:54.560 に答える
1

パラメータタイプとしてのリストは、パラメータが任意のオブジェクトタイプのアイテムのリストでなければならないことを示しています。さらに、Eパラメーターをバインドして、関数本体内のリスト項目への参照を宣言できます。

パラメータタイプとしてのリストは、を使用する以外にリスト内のアイテムへの参照を宣言する方法がないことを除いて、同じセマンティクスを持っていますObject。他の投稿では、さらに微妙な違いがあります。

于 2012-06-08T04:42:23.493 に答える
1

私は通常、< E >と< の違いを説明します。>論理的数量化、つまり全称記号と存在記号との比較による。

  • 「forallE、...」に対応
  • 「……のようなもの(で表す)が存在する」に相当します。

したがって、次のジェネリックメソッド宣言は、すべてのクラスタイプEについて、次のように定義することを意味します。funct1

public static <E> void funct1  (List<E>; list1) {

}

次のジェネリックメソッド宣言は、< >、を定義しますfunct2

public static void funct2(List<?> list) {

}
于 2014-12-28T12:13:33.673 に答える
0

1つ目は、Eタイプのアイテムのリストでなければならないパラメーターを受け入れる関数です。

2番目の例のタイプは定義されていません

List<?> list

したがって、任意のタイプのオブジェクトのリストを渡すことができます。

于 2012-06-08T05:21:32.427 に答える
0

(編集してから)これらの2つの関数シグネチャは、外部コードに対して同じ効果があります。どちらもList引数として任意のものを取ります。ワイルドカードは、1回だけ使用される型パラメーターと同等です。

于 2012-06-08T07:28:38.160 に答える
0

前述の違いに加えて、追加の違いもあります。ジェネリックメソッドの呼び出しに対して型引数を明示的に設定できます。

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はメソッドを含むクラスの名前です。)

于 2015-05-24T10:32:08.393 に答える
0

このコンテキストでは、ワイルドカード(?)とタイプパラメーター(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にはいくつかの機能があります。

ワイルドカード(?)

  1. タイプパラメータ(E)は上限のみをサポートしますが、タイプの上限と下限をサポートします。

タイプParam(E)

  1. 時々、実際のタイプを渡す必要はありません。例:

    ArrayList<Integer> ai = new ArrayList<Integer>();
    ArrayList<Double>  ad = new ArrayList<Double>();
    function2(ai, ad);
    //It will compile and the T will be Number.
    

この場合、コンパイラは実際の引数の型に基づいて型引数を推測します

于 2021-09-23T13:55:55.440 に答える