5

私は質問q1q2q3を調べましたが、それらは私の質問を正確にカバーしていません。

ArrayList<A> and ArrayList<? extends A>変数またはパラメーターを宣言するために使用されることに注意してください(新しいジェネリッククラスを作成するためではありません)。

オブジェクト属性を宣言するとき(ケース1)、両方の式は同等ですか?:

class Foo {

  private ArrayList<A> aList; // == ArrayList<? extends A> aList;
}

編集:どちらの式も、どのような種類のオブジェクトを追加できるかという観点からは同等ですがaList、次の場合と同じ意味で異なりますか?

ただし、パラメータ宣言で使用する場合は異なります(ケース2)?:

void methodFoo(ArrayList<A> al)  !=  void methodFoo(ArrayList<? extends A> al)

ArrayList<A1>最初のオブジェクトはArrayListオブジェクトの受け渡しのみを許可し、2番目のオブジェクトは送信を許可する「より寛容な」ようなものであるため ArrayList<A2>(A1とA2がAを拡張する限り)?

これが正しければ、2つの表現が事実上異なる他のシナリオはありますか?

ありがとう、

4

6 に答える 6

8

いくつかの実用的な例を見てみましょう。言う、あなたは持っています:

List<Number> list;

これは、この変数またはフィールドに割り当てられたものがすべて取得Numberおよび出力されることを意味するNumberため、何を期待するかを常に把握できます。extendsIntegerから、このリストに追加できます。ただし、たとえば、このリストに割り当てることはできません。IntegerNumberArrayList<Long>

しかし、この場合を考えてみましょう。

List<? extends Number> list;

これは言う:ねえ、それは拡張するもののリストですがNumber、誰も正確に何を知っていません。これは何を意味するのでしょうか?これは、たとえば、ArrayList<Long>最初のケースでは割り当てられなかったこのリストに割り当てることができることを意味します。このリストが出力するものはすべて、になることはまだわかっていますが、これ以上リストに入れることNumberはできません。Integer

逆の場合もあります。

List<? super Number> list;

あなたが言うことを印刷することによって:それはNumberまたはそのスーパークラスのリストです。これはすべてが逆になるところです。リストはとを参照できるようにArrayList<Object>なりArrayList<Number>ました。このリストが何を出力するかはわかりません。でしょNumberうか?でしょObjectうか?しかし、これで、このリストとlikeまたは。のサブクラスにを入れることができることがわかりました。NumberNumberIntegerLong

ちなみに、プロデューサーはコンシューマースーパー(略してPECS)を拡張するというルールがあります。値を出力するためにリストが必要な場合、それはプロデューサーです。これは2番目のケースです。値を受け入れるためにリストが必要な場合、それはコンシューマーです。これは3番目のケースです。両方が必要な場合は、ワイルドカードを使用しないでください(これが最初のケースです)。

これで問題が解決することを願っています。

于 2012-04-14T10:40:26.653 に答える
4

これは違いを説明します:

public class GenericsTest {
  private ArrayList<A> la;
  private ArrayList<? extends A> lexta;

  void doListA(ArrayList<A> la) {}
  void doListExtA(ArrayList<? extends A> lexta) {}

  void tester() {
    la = new ArrayList<SubA>(); // Compiler error: Type mismatch
    doListA(new ArrayList<SubA>());  // Compiler error: Type mismatch
    lexta = new ArrayList<SubA>();
    doListExtA(new ArrayList<SubA>());
  }

  static class A {}
  static class SubA extends A {}
}

ご覧のとおり、メソッドの呼び出しと変数/インスタンスフィールドの割り当てには同じルールがあります。メソッド呼び出しを、宣言されたパラメーターへの引数の割り当てとして見てください。

于 2012-04-14T10:39:44.473 に答える
1

ArrayList<A>は特定のクラスAを意味しますが、ArrayList<? extends A>クラスAまたはAを拡張する任意のクラス(Aのサブクラス)を意味します。これにより、より一般的なものになります。

于 2012-04-14T10:04:22.467 に答える
1

変数宣言として使用private ArrayList<A> aList;することは、ワイルドカードを使用することと実際には同等ではありませんprivate ArrayList<? extends A> aList;

ワイルドカードバージョンでは、AおよびA自体を拡張するタイプのArrayListを割り当てることができますが、タイプセーフかどうかを判断できないため、リストへの要素の追加を拒否します。ArrayList<A>一方、では、タイプAのArrayList(またはArrayListの拡張)のみを割り当てることができ、A要素とAを拡張する要素を追加できます。

List<A>参考:またはのような変数/パラメータを宣言するには、より抽象型を使用することをお勧めしますCollection<A>

于 2012-04-14T10:24:01.420 に答える
0

最初は理解するのが難しいですが、継承はジェネリックには適用されません。つまり、BがAを拡張する場合List<B>、の「サブクラス」ではありません(割り当てることはできません)List<A>。さらに、List<? extends A>は(に割り当てることができない)の「サブクラス」ではありませんList<A>

于 2012-04-14T10:54:23.543 に答える
0

主な違いは、汎用形式が基本クラス(またはインターフェイス)のメソッドで引数または戻り型として使用される場合、オーバーロードではなく、より広範囲の型シグネチャをオーバーライドとしてカウントできることです。

関数オーバーライド-Javaでのオーバーロード

たとえば、次のコードは有効です(Java 7の場合)。

interface A
{
    List<? extends Number> getSomeNumbers();
}

class B implements A
{
    @Override
    public ArrayList<Integer> getSomeNumbers()
    {
        return new ArrayList<>();
    }
}

列挙の違い<?ZipEntry>とEnumeration<ZipEntry>を拡張しますか?

これはすべて、他の誰かが使用しているときにキャストが少なくて済むコードを記述できる場合があることを意味します。これは、彼らがしなければならないタイピングの量を減らすだけでなく、起こりうる失敗を排除するはずです。

もちろん、Javaジェネリックの問題は、下位互換性によって制約された方法で導入されたことです。したがって、すべてが機能すると思われるとは限りません。正確に機能するものと機能しないものについては、詳細がかなり複雑になります。

http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ812

于 2012-04-14T11:22:25.920 に答える