91

最近、この記事を読みました: http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html

私の質問は、次のようなメソッドを作成する代わりに:

public void drawAll(List<? extends Shape> shapes){
    for (Shape s: shapes) {
        s.draw(this);
    }
}

私はこのようなメソッドを作成することができ、それは正常に動作します:

public <T extends Shape> void drawAll(List<T> shapes){
    for (Shape s: shapes) {
        s.draw(this);
    }
}

どの方法を使用すればよいですか? この場合、ワイルドカードは役に立ちますか?

4

5 に答える 5

143

それはあなたが何をする必要があるかによって異なります。次のようなことをしたい場合は、境界型パラメーターを使用する必要があります。

public <T extends Shape> void addIfPretty(List<T> shapes, T shape) {
    if (shape.isPretty()) {
       shapes.add(shape);
    }
}

ここに aList<T> shapesと aT shapeがあるので、安全に できshapes.add(shape)ます。宣言されている場合、安全にList<? extends Shape>アクセスすることはできませんadd( と がある可能性があるためList<Square>) Circle

したがって、境界型パラメーターに名前を付けることで、ジェネリック メソッドの他の場所で使用するオプションが得られます。もちろん、この情報は常に必要なわけではありません。そのため、タイプ (たとえば、あなたの ) についてそれほど知る必要がない場合は、drawAllワイルドカードだけで十分です。

境界型パラメーターを再度参照していない場合でも、複数の境界がある場合は、境界型パラメーターが必要です。以下は、 Angelika Langer の Java Generics FAQからの引用です。

ワイルドカード バウンドと型パラメーター バウンドの違いは何ですか?

ワイルドカードは 1 つの境界のみを持つことができますが、型パラメーターは複数の境界を持つことができます。ワイルドカードには上限または下限を指定できますが、型パラメーターには下限などはありません。

ワイルドカードの境界と型パラメーターの境界は、どちらも境界と呼ばれ、構文の一部が似ているため、混同されることがよくあります。[…]

構文:

  type parameter bound     T extends Class & Interface1 & … & InterfaceN

  wildcard bound  
      upper bound          ? extends SuperType
      lower bound          ? super   SubType

ワイルドカードに指定できる境界は、下限または上限のいずれか 1 つだけです。ワイルドカード境界のリストは許可されていません。

対照的に、型パラメーターは複数の境界を持つことができますが、型パラメーターの下限などはありません。

有効な Java 2nd Edition、項目 28からの引用: API の柔軟性を高めるには、境界付きワイルドカードを使用します。

最大限の柔軟性を得るには、プロデューサーまたはコンシューマーを表す入力パラメーターでワイルドカード タイプを使用します。[…] PECS は生産者extends、消費者の略super[…]

戻り値の型としてワイルドカード型を使用しないでください。ユーザーに追加の柔軟性を提供するのではなく、クライアント コードでワイルドカード型を使用することを強制します。適切に使用されたワイルドカード型は、クラスのユーザーにはほとんど見えません。これらは、メソッドが受け入れるべきパラメーターを受け入れ、拒否すべきパラメーターを拒否するようにします。クラスのユーザーがワイルドカード型について考える必要がある場合は、クラスの API に問題がある可能性があります

PECS の原則を適用して、addIfPretty例に戻り、次のように記述してより柔軟にすることができます。

public <T extends Shape> void addIfPretty(List<? super T> list, T shape) { … }

これでaddIfPretty、たとえば、 aCircleから a にできList<Object>ます。これは明らかにタイプセーフですが、元の宣言はそれを許容するほど柔軟ではありませんでした。

関連する質問


概要

  • 境界型パラメーター/ワイルドカードを使用してください。API の柔軟性が向上します。
  • 型に複数のパラメーターが必要な場合は、バインドされた型パラメーターを使用するしかありません
  • 型に下限が必要な場合は、制限付きワイルドカードを使用するしかありません
  • 「生産者」には上限があり、「消費者」には下限があります
  • 戻り値の型にワイルドカードを使用しないでください
于 2010-08-15T08:29:21.500 に答える
6

あなたの例では、他の場所ではTを使用しないため、実際にTを使用する必要はありません。

しかし、あなたが次のようなことをした場合:

public <T extends Shape> T drawFirstAndReturnIt(List<T> shapes){
    T s = shapes.get(0);
    s.draw(this);
    return s;
}

または、polygenlubricantsが言ったように、リスト内のタイプパラメータを別のタイプパラメータと一致させたい場合は、次のようにします。

public <T extends Shape> void mergeThenDraw(List<T> shapes1, List<T> shapes2) {
    List<T> mergedList = new ArrayList<T>();
    mergedList.addAll(shapes1);
    mergedList.addAll(shapes2);
    for (Shape s: mergedList) {
        s.draw(this);
    }
}

最初の例では、Shapeの子をとる可能性のある関数に結果を渡すことができるため、Shapeだけを返すよりも、型の安全性が少し高くなります。たとえばList<Square>、私のメソッドにaを渡してから、結果のSquareをSquaresのみを使用するメソッドに渡すことができます。'?'を使用した場合 結果のシェイプを正方形にキャストする必要がありますが、これはタイプセーフではありません。

2番目の例では、両方のリストが同じ型パラメーターを持っていることを確認し(それぞれの「?」は異なるため、「?」では実行できません)、両方のリストのすべての要素を含むリストを作成できます。 。

于 2010-08-15T08:50:54.193 に答える
2

2 つの SinglyLinkQueue をマージしたい、James Gosling による Java プログラミング第 4 版の次の例を検討してください。

public static <T1, T2 extends T1> void merge(SinglyLinkQueue<T1> d, SinglyLinkQueue<T2> s){
    // merge s element into d
}

public static <T> void merge(SinglyLinkQueue<T> d, SinglyLinkQueue<? extends T> s){
        // merge s element into d
}

上記の方法はどちらも同じ機能を持っています。では、どちらが好ましいでしょうか?答えは2枚目です。著者自身の言葉で:

「ワイルドカードを使用したコードは一般に、複数の型パラメーターを使用したコードよりも読みやすいため、一般的なルールはワイルドカードを使用することです。型変数が必要かどうかを判断するときは、その型変数が2つ以上のパラメーターを関連付けるために使用されているかどうかを自問してください。または、パラメーターの型を戻り値の型に関連付けます。答えが「いいえ」の場合は、ワイルドカードで十分です。"

注: 本では 2 番目のメソッドのみが指定されており、型パラメーター名は 'T' ではなく S です。最初の方法は本にはありません。

于 2015-07-19T00:26:20.217 に答える
1

私が理解している限り、ワイルドカードを使用すると、型パラメーターが必要ない状況でより簡潔なコードを使用できます (たとえば、複数の場所で参照されているため、または他の回答で詳しく説明されているように複数の境界が必要なため)。

あなたが示したリンクで、私は(「ジェネリックメソッド」の下で)この方向を示唆する次のステートメントを読みました:

ジェネリック メソッドを使用すると、型パラメーターを使用して、メソッドへの 1 つまたは複数の引数の型とその戻り値の型の間の依存関係を表現できます。そのような依存関係がない場合は、ジェネリック メソッドを使用しないでください。

[...]

ワイルドカードを使用すると、明示的な型パラメーターを宣言するよりも明確で簡潔になるため、可能な限り優先する必要があります。

[...]

ワイルドカードには、フィールド、ローカル変数、および配列の型として、メソッド シグネチャの外で使用できるという利点もあります。

于 2018-03-15T13:41:04.517 に答える
0

2 番目の方法はもう少し冗長ですが、T内部 を参照できます。

for (T shape : shapes) {
    ...
}

私が理解している限り、それが唯一の違いです。

于 2010-08-15T08:28:18.290 に答える