1

Joshua Bloch の著書「Effective Java」によると、ジェネリクスで制限付きワイルドカードをいつどのように使用するかについてのルールがあります。このルールが PECS (Producer-Extends, Comsumer-Super) です。次の例を調べると:

Stack<Number> numberStack = new Stack<Number>();
Iterable<Integer> integers = ... ;
numberStack.pushAll(integers);

このルールがこの例に完全に適合することを理解しています。pushAllメソッドを次のサンプルとして宣言する必要があります。

// Wildcard type for parameter that serves as an E producer
public void pushAll(Iterable<? extends E> src) {
    for (E e : src)
    {
       push(e);
    }  
}

しかし、次の例があるとどうなりますか?

Stack<Integer> integerStack = new Stack<Integer>();
Iterable<Number> numbers = ... ;
integerStack.pushAll(numbers);

次のように宣言する必要がpushAllあります。

public void pushAll(Iterable<? super E> src) {
    for (E e : src)
    {  
        push(e);
    }
}

PECS規則によれば、上記の宣言は誤りです。しかし、私はStackofを持ち、Integerこれに渡したいStacka Number。なぜそれをしないのですか? キーワード
を常に使用する必要があるのはなぜですか? extends使い方superが間違っているのはなぜですか?
もちろん、消費者の視点も同じです。なぜ消費者は常にあるべきなのsuperですか?


PS: より具体的に言うと、上記の例は、参照されている本のセクション「項目 28」にあります。

4

5 に答える 5

3

a を宣言するときStack<Foo>は、Foo のスタック、または Foo のサブクラスを意味します。String例として、aを aに入れることができると期待するでしょうStack<Object>。他の方法は正しくありません。別のオブジェクトを に挿入することはできませんStack<String>

あなたの例では、を宣言しStack<Integer>ます。このスタックに整数を入れることはできますが、パラメーターを宣言した場合は他の数値 (Double など) を入れることはできません<? super E>。そのため、put-メソッドには type のパラメーターが必要<? extends E>です。

于 2013-06-05T11:21:39.437 に答える
1

Number は Integer 以外のものである可能性があるため、Stack に任意の数値を格納しようとしてもうまくいきません。したがって、あなたの例はあまり意味がありません。

オブジェクトがコンシューマーとして機能する場合、つまり、オブジェクトのジェネリック型のインスタンスが引数としてオブジェクトのメソッドに渡される場合は、super を使用します。例えば:

 Collections.sort(List<T>, Comparator<? super T>)

この例では、sort メソッドはコレクションから T 個のインスタンスを取得し、それらを引数としてcompare(T o1, T o2)コンパレーターの に渡します。

これを、Iterablesrcがプロデューサーである最初の例と比較してください。このpushAll()メソッドは、T のインスタンスを生成する (つまり、返す) Iterable のメソッドを呼び出します。この場合、iterable はプロデューサーであるため、? extends T

于 2013-06-05T11:24:54.440 に答える
0

あなたの例はあまり意味がありません。次のような構造<? extends Number>は、Number および Number から継承するすべての型が許可されることを意味します。したがって、タイプ Number から最も具体的なものまで、上限と下限を定義します。逆に言え<? super Number>ば、Number とそのスーパータイプのいずれかが許可されることを意味します。Number は Object を拡張し、Serializable を実装するため、次の 3 つの型が許可されます。

  1. java.lang.Number
  2. java.lang.Object
  3. java.io.Serializable

あなたの例では、ジェネリック型を宣言していますStack<Integer>。以下を考えてみましょう。

  1. あなたのスタックは、整数のスーパータイプのアイテムを保持することはできません
  2. Integer クラスは final であるため、サブクラス化できないため、Stack はInteger のサブタイプのアイテムを保持できません。

したがって、ジェネリック型を宣言する場合Stack<Integer>、イテラブルは型Iterable<Integer>であるため、Stack はInteger 型のアイテムのみを保持できます。ニーモニックPECSで完全に正しいですが、これは、少なくとも 1 つのスーパー タイプと少なくとも 1 つのサブタイプを持つ具体的なタイプを選択した場合にのみ機能します。

于 2013-06-05T11:51:17.610 に答える
0

pushAllメソッドでは、 type を渡すのではなく、をE拡張する任意の型を渡しますE。したがって、 の を渡す代わりに、Iterableを拡張するNumber任意の型を渡すことができます。IterableNumber

元の例では、 などのNumberサブクラスである任意の型を渡すことができるため、型を使用しています。NumberIntegerBigDecimal

あなたの例では、逆にやっています。Integerを宣言するために使用していますStack。したがって、pushAllによって拡張されたクラスのみを受け入れることができIntegerます。Numbers(または他のクラス、Integer最終クラスであるため)を使用することはできません。

于 2013-06-05T11:22:22.820 に答える