web-dev-qa-db-ja.com

<?の違いスーパーT>と<? JavaでT>を拡張します。

List<? super T>List<? extends T>の違いは何ですか?

私はList<? extends T>を使用していましたが、それに要素を追加することはできませんlist.add(e)List<? super T>はそうしています。

689
Anand

extends

ワイルドカード宣言のList<? extends Number> foo3は、これらのいずれもが正当な割り当てであることを意味します。

List<? extends Number> foo3 = new ArrayList<Number>();  // Number "extends" Number (in this context)
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>();  // Double extends Number
  1. Reading - 上記の可能な割り当てを考えると、List foo3からどのタイプのオブジェクトを読み込むことが保証されているのでしょうか。

    • foo3に割り当てることができるリストには、NumberまたはNumberのサブクラスが含まれているため、Numberを読み取ることができます。
    • foo3List<Double>を指している可能性があるため、Integerを読み取ることはできません。
    • foo3List<Integer>を指している可能性があるため、Doubleを読み取ることはできません。
  2. Writing - 上記の可能な代入を考えて、 all 上記の可能なArrayList代入に合法となる、どのタイプのオブジェクトをList foo3に追加できますか。

    • foo3List<Double>を指す可能性があるため、Integerを追加することはできません。
    • foo3List<Integer>を指す可能性があるため、Doubleを追加することはできません。
    • foo3List<Integer>を指す可能性があるため、Numberを追加することはできません。

List<? extends T>にオブジェクトを追加することはできません。なぜなら、それが実際にどのような種類のListを指しているのか保証できないため、そのList内でそのオブジェクトが許可されることを保証できません。あなたはそこからしか読むことができず、あなたはTまたはTのサブクラスを得るでしょう。

super

List <? super T>を考えてください。

ワイルドカード宣言のList<? super Integer> foo3は、これらのいずれもが正当な割り当てであることを意味します。

List<? super Integer> foo3 = new ArrayList<Integer>();  // Integer is a "superclass" of Integer (in this context)
List<? super Integer> foo3 = new ArrayList<Number>();   // Number is a superclass of Integer
List<? super Integer> foo3 = new ArrayList<Object>();   // Object is a superclass of Integer
  1. Reading - 上記の可能な割り当てを考えると、あなたがList foo3から読んだときどのような種類のオブジェクトを受け取ることが保証されていますか:

    • foo3List<Number>またはList<Object>を指している可能性があるため、Integerは保証されていません。
    • foo3List<Object>を指す可能性があるため、Numberは保証されません。
    • only の保証は、ObjectまたはObjectのサブクラスのインスタンスを取得するということです(ただし、どのサブクラスかはわかりません)。
  2. Writing - 上記の可能な代入を考えて、 all 上記の可能なArrayList代入に合法となる、どのタイプのオブジェクトをList foo3に追加できますか。

    • Integerは上記のリストのいずれでも使用できるため、Integerを追加できます。
    • Integerのサブクラスのインスタンスは上記のいずれのリストでも許可されているため、Integerのサブクラスのインスタンスを追加できます。
    • foo3ArrayList<Integer>を指す可能性があるため、Doubleを追加することはできません。
    • foo3ArrayList<Integer>を指す可能性があるため、Numberを追加することはできません。
    • foo3ArrayList<Integer>を指す可能性があるため、Objectを追加することはできません。

PECS

PECSを忘れないでください: "プロデューサーの拡張、スーパーコンシューマ"

  • "Producer Extends" - Listの値を生成するためにTが必要な場合(リストからTsを読みたい場合)、? extends Tで宣言する必要があります。 List<? extends Integer>。しかし、あなたはこのリストに追加することはできません。

  • "Consumer Super" - Listの値を消費するためにTが必要な場合(Tsをリストに書き込みたい場合)、? super Tで宣言する必要があります。 List<? super Integer>。しかし、このリストからどんな種類のオブジェクトを読み取ることができるかについての保証はありません。

  • リストから読み書きする必要がある場合は、ワイルドカードを使用せずに正確に宣言する必要があります。 List<Integer>

注意 この例は、Java Generics FAQ からのものです。ソースリストsrc(生成リスト)がextendsを使用し、宛先リストdest(消費リスト)がsuperを使用する方法に注意してください。

public class Collections { 
  public static <T> void copy(List<? super T> dest, List<? extends T> src) {
      for (int i = 0; i < src.size(); i++) 
        dest.set(i, src.get(i)); 
  } 
}

も参照してください。どうすればList <に追加できますか?数>データ構造を拡張しますか?

1283
Bert F

この階層を想像してみてください

enter image description here

1.拡張する

書くことで

    List<? extends C2> list;

あなたはlistが(例えば)ArrayListのジェネリック型がC2の7 subtypes のいずれかである(C2が含まれる)型のオブジェクトを参照できると言っています:

  1. C2:new ArrayList<C2>();、(C2またはサブタイプを格納できるオブジェクト)または
  2. D1:new ArrayList<D1>();、(D1またはサブタイプを格納できるオブジェクト)または
  3. D2:new ArrayList<D2>();、(D2またはサブタイプを格納できるオブジェクト)または...

等々。 7つの異なるケース

    1) new ArrayList<C2>(): can store C2 D1 D2 E1 E2 E3 E4
    2) new ArrayList<D1>(): can store    D1    E1 E2  
    3) new ArrayList<D2>(): can store       D2       E3 E4
    4) new ArrayList<E1>(): can store          E1             
    5) new ArrayList<E2>(): can store             E2             
    6) new ArrayList<E3>(): can store                E3             
    7) new ArrayList<E4>(): can store                   E4             

可能性のあるそれぞれのケースに対して、「格納可能な」タイプのセットがあります。

enter image description here

ご覧のとおり、すべての場合に共通のsafe typeはありません。

  • list.add(new C2(){});かもしれないのでlist = new ArrayList<D1>();はできません
  • list.add(new D1(){});かもしれないのでlist = new ArrayList<D2>();はできません

等々。

スーパー

書くことで

    List<? super C2> list;

あなたはlistが(例えば)ArrayListの総称型が7 スーパータイプ of C2C2が含まれる)のいずれかであるオブジェクトを参照できると言っているのです。

  • A1:new ArrayList<A1>();、(A1またはサブタイプを格納できるオブジェクト)または
  • A2:new ArrayList<A2>();、(A2またはサブタイプを格納できるオブジェクト)または
  • A3:new ArrayList<A3>();、(A3またはサブタイプを格納できるオブジェクト)または...

等々。 7つの異なるケース

    1) new ArrayList<A1>(): can store A1          B1 B2       C1 C2    D1 D2 E1 E2 E3 E4
    2) new ArrayList<A2>(): can store    A2          B2       C1 C2    D1 D2 E1 E2 E3 E4
    3) new ArrayList<A3>(): can store       A3          B3       C2 C3 D1 D2 E1 E2 E3 E4
    4) new ArrayList<A4>(): can store          A4       B3 B4    C2 C3 D1 D2 E1 E2 E3 E4
    5) new ArrayList<B2>(): can store                B2       C1 C2    D1 D2 E1 E2 E3 E4
    6) new ArrayList<B3>(): can store                   B3       C2 C3 D1 D2 E1 E2 E3 E4
    7) new ArrayList<C2>(): can store                            C2    D1 D2 E1 E2 E3 E4

可能性のあるそれぞれのケースに対して、「格納可能な」タイプのセットがあります。

enter image description here

ご覧のとおり、ここでは7つの安全な型があり、これらはすべての場合に共通です:C2D1D2E1E2E3E4

  • 参照しているリストの種類にかかわらず、C2が許可されているため、list.add(new C2(){});を使用できます。
  • 参照しているリストの種類にかかわらず、D1が許可されているため、list.add(new D1(){});を使用できます。

等々。お気づきのとおり、これらの型はC2型から始まる階層に対応しています。

ノート

いくつかのテストを行いたい場合は、ここで完全な階層

interface A1{}
interface A2{}
interface A3{}
interface A4{}

interface B1 extends A1{}
interface B2 extends A1,A2{}
interface B3 extends A3,A4{}
interface B4 extends A4{}

interface C1 extends B2{}
interface C2 extends B2,B3{}
interface C3 extends B3{}

interface D1 extends C1,C2{}
interface D2 extends C2{}

interface E1 extends D1{}
interface E2 extends D1{}
interface E3 extends D2{}
interface E4 extends D2{}
201
Luigi Cortese

@Bert Fからの回答が大好きですが、これが私の脳の見方です。

手にXがあります。 write私のXをリストにしたい場合、そのリストは、Xのリストか、Xを書き込むときにアップキャストできるもののリストのいずれかでなければなりませんスーパークラス X ...

List<? super   X>

リストを取得して、そのリストからreadをXにしたい場合は、Xのリスト、またはXにアップキャストできるもののリスト(つまり、何でも)そのextends X

List<? extends X>

お役に立てれば。

77

Bert F's answer 私の理解に基づいて説明したい。

として3つのクラスがあるとしましょう。

public class Fruit{}

public class Melon extends Fruit{}

public class WaterMelon extends Melon{}

ここにあります

List<? extends Fruit> fruitExtendedList = …

//Says that I can be a list of any object as long as this object extends Fruit.

[Ok]をクリックして、fruitExtendedListから値を取得してみましょう。

Fruit fruit = fruitExtendedList.get(position)

//This is valid as it can only return Fruit or its subclass.

もう一度試してみましょう

Melon melon = fruitExtendedList.get(position)

//This is not valid because fruitExtendedList can be a list of Fruit only, it may not be 
//list of Melon or WaterMelon and in Java we cannot assign sub class object to 
//super class object reference without explicitly casting it.

同じことが当てはまります。

WaterMelon waterMelon = fruitExtendedList.get(position)

それでは、fruitExtendedListにオブジェクトを設定してみましょう。

フルーツオブジェクトを追加する

fruitExtendedList.add(new Fruit())

//This in not valid because as we know fruitExtendedList can be a list of any 
//object as long as this object extends Fruit. So what if it was the list of  
//WaterMelon or Melon you cannot add Fruit to the list of WaterMelon or Melon.

メロンオブジェクトを追加する

fruitExtendedList.add(new Melon())

//This would be valid if fruitExtendedList was the list of Fruit but it may 
//not be, as it can also be the list of WaterMelon object. So, we see an invalid 
//condition already.

最後にWaterMelonオブジェクトを追加してみましょう

fruitExtendedList.add(new WaterMelon())

//Ok, we got it now we can finally write to fruitExtendedList as WaterMelon 
//can be added to the list of Fruit or Melon as any superclass reference can point 
//to its subclass object.

しかし待つもし誰かが新しいタイプのLemonを作ることを決めた場合、引数としてSaltyLemonとすることができます

public class SaltyLemon extends Lemon{}

これでfruitExtendedListはFruit、Melon、WaterMelonまたはSaltyLemonのリストになります

だから、私たちの声明

fruitExtendedList.add(new WaterMelon())

どちらも無効です。

基本的に、fruitExtendedListには何も記述できないと言えます。

これはList<? extends Fruit>を要約します

今見てみましょう

List<? super Melon> melonSuperList= …

//Says that I can be a list of anything as long as its object has super class of Melon.

では、melonSuperListから値を取得してみましょう。

Fruit fruit = melonSuperList.get(position)

//This is not valid as melonSuperList can be a list of Object as in Java all 
//the object extends from Object class. So, Object can be super class of Melon and 
//melonSuperList can be a list of Object type

同様にMelon、WaterMelonまたは他のどのオブジェクトも読むことができません。

しかし、Object型のインスタンスを読むことができることに注意してください。

Object myObject = melonSuperList.get(position)

//This is valid because Object cannot have any super class and above statement 
//can return only Fruit, Melon, WaterMelon or Object they all can be referenced by
//Object type reference.

それでは、melonSuperListから値を設定してみましょう。

オブジェクト型オブジェクトの追加

melonSuperList.add(new Object())

//This is not valid as melonSuperList can be a list of Fruit or Melon.
//Note that Melon itself can be considered as super class of Melon.

フルーツタイプオブジェクトの追加

melonSuperList.add(new Fruit())

//This is also not valid as melonSuperList can be list of Melon

メロン型オブジェクトの追加

melonSuperList.add(new Melon())

//This is valid because melonSuperList can be list of Object, Fruit or Melon and in 
//this entire list we can add Melon type object.

WaterMelon型オブジェクトの追加

melonSuperList.add(new WaterMelon())

//This is also valid because of same reason as adding Melon

要約すると、melonSuperListにMelonまたはそのサブクラスを追加して、オブジェクト型オブジェクトのみを読み取ることができます。

17
Sushant

superは下限で、extendsは上限です。

http://download.Oracle.com/javase/tutorial/extra/generics/morefun.htmlによると

解決策は、まだ見たことのない形式の境界ワイルドカードを使用することです。下限を持つワイルドカード。構文は?スーパーTは、Tのスーパータイプである未知のタイプを表します(またはT自体。スーパータイプの関係は再帰的であることを忘れないでください)。これは、これまで使用してきたワイルドカードの2つです。どこで使用しますか。 Tのサブタイプである未知のタイプを表すために、Tを拡張します。

14
Istao

違いを視覚化したいのですが。我々が持っていると仮定します。

class A { }
class B extends A { }
class C extends B { }

List<? extends T> - 読み取りと割り当て

|-------------------------|-------------------|---------------------------------|
|         wildcard        |        get        |              assign             |
|-------------------------|-------------------|---------------------------------|
|    List<? extends C>    |    A    B    C    |   List<C>                       |
|-------------------------|-------------------|---------------------------------|
|    List<? extends B>    |    A    B         |   List<B>   List<C>             |
|-------------------------|-------------------|---------------------------------|
|    List<? extends A>    |    A              |   List<A>   List<B>   List<C>   |
|-------------------------|-------------------|---------------------------------|

List<? super T> - 書き込みと代入

|-------------------------|-------------------|-------------------------------------------|
|         wildcard        |        add        |                   assign                  |
|-------------------------|-------------------|-------------------------------------------|
|     List<? super C>     |    C              |  List<Object>  List<A>  List<B>  List<C>  |
|-------------------------|-------------------|-------------------------------------------|
|     List<? super B>     |    B    C         |  List<Object>  List<A>  List<B>           |
|-------------------------|-------------------|-------------------------------------------|
|     List<? super A>     |    A    B    C    |  List<Object>  List<A>                    |
|-------------------------|-------------------|-------------------------------------------|

すべての場合において:

  • ワイルドカードに関係なく、リストからいつでも get Objectを取得できます。
  • ワイルドカードに関係なく、いつでも - nullを可変リストに追加できます。
9
Oleksandr

リストに項目を追加する:

  • リスト<? Xを拡張 - > はリストにnullを除いて、何も追加することを許可しない。

  • リスト<? super X> - X(Xまたはそのサブタイプ)、またはnullのものをすべて追加できます。

リストからアイテムを取得する:

  • List <からアイテムを取得したときX> を拡張すると、それをX型の変数、またはObjectを含むXの任意のスーパータイプに割り当てることができます。
  • List <からアイテムを取得したときsuper X> 、あなたはそれをObject型の変数にのみ割り当てることができます。

いくつかの例:

    List<? extends Number> list1 = new ArrayList<Integer>();
    list1.add(null);  //OK
    Number n = list1.get(0);  //OK
    Serializable s = list1.get(0);  //OK
    Object o = list1.get(0);  //OK

    list1.add(2.3);  //ERROR
    list1.add(5);  //ERROR
    list1.add(new Object());  //ERROR
    Integer i = list1.get(0);  //ERROR

    List<? super Number> list2 = new ArrayList<Number>();
    list2.add(null);  //OK
    list2.add(2.3);  //OK
    list2.add(5);  //OK
    Object o = list2.get(0);  //OK

    list2.add(new Object());  //ERROR
    Number n = list2.get(0);  //ERROR
    Serializable s = list2.get(0);  //ERROR
    Integer i = list2.get(0);  //ERROR
3
elyor

extends を使うとコレクションからのみ取得できます。入れることはできません。また、 super はgetとputの両方を許可しますが、get中の戻り型は ?です。スーパーT

3
Sai Sunder

ここで最も紛らわしいことは、私たちが指定した型制限がどのようなものであっても、代入は一方向にしか機能しないということです。

baseClassInstance = derivedClassInstance;

Integer extends NumberInteger<? extends Number>として機能すると考えるかもしれませんが、コンパイラは<? extends Number> cannot be converted to Integer(つまり、人間の言い方では、数値を拡張するものはすべて整数に変換できるのは間違っていると教えてくれます。 ):

class Holder<T> {
    T v;
    T get() { return v; }
    void set(T n) { v=n; }
}
class A {
    public static void main(String[]args) {
        Holder<? extends Number> he = new Holder();
        Holder<? super Number> hs = new Holder();

        Integer i;
        Number n;
        Object o;

        // Producer Super: always gives an error except
        //       when consumer expects just Object
        i = hs.get(); // <? super Number> cannot be converted to Integer
        n = hs.get(); // <? super Number> cannot be converted to Number
                      // <? super Number> cannot be converted to ... (but
                      //       there is no class between Number and Object)
        o = hs.get();

        // Consumer Super
        hs.set(i);
        hs.set(n);
        hs.set(o); // Object cannot be converted to <? super Number>

        // Producer Extends
        i = he.get(); // <? extends Number> cannot be converted to Integer
        n = he.get();
        o = he.get();

        // Consumer Extends: always gives an error
        he.set(i); // Integer cannot be converted to <? extends Number>
        he.set(n); // Number cannot be converted to <? extends Number>
        he.set(o); // Object cannot be converted to <? extends Number>
    }
}

Integerは任意のスーパークラスのNumberに変換できるため、hs.set(i);は問題ありません(IntegerNumberのスーパークラスであるためではありません)。

EDITはConsumer ExtendsとProducer Superについてのコメントを追加しました - それらはnothingObjectを指定しているので意味がありません。 CEPSは役に立ちませんので、PECSを覚えておくことをお勧めします。

.add()'<?>''<? extends>'、そして部分的に'<? super>'に制限されている理由を理解するために上記のすべての答えを調べることができます。

あなたがそれを覚えておきたいと思うなら、ここにそれのすべての結論があります、そして毎回答えを探りに行きたくはありません:

List<? extends A>は、これがListのすべてのAおよびAのサブクラスを受け入れることを意味します。しかし、このリストには何も追加できません。 A型のオブジェクトすらありません。

List<? super A>は、これがAのリストとAのスーパークラスを受け入れることを意味します。 A型のオブジェクトとそのサブクラスを追加できます。

2
jforex78

拡張を使用し、super を使用する場合

ワイルドカードは、メソッドパラメータで最も役立ちます。それらはメソッドインターフェースに必要な柔軟性を可能にします。

拡張を使うときとスーパーバウンドを使うときを混同している人はよくいます。経験則は、原則です。パラメータ化されたコンテナから何かを取得したら、extendsを使用してください。

int totalFuel(List<? extends Vehicle> list) {
int total = 0;
for(Vehicle v : list) {
    total += v.getFuel();
}
return total;}

メソッドtotalFuelはリストからVehiclesを取得し、燃料の量について質問し、合計を計算します。オブジェクトをパラメータ化されたコンテナに入れる場合は、superを使用してください。

int totalValue(Valuer<? super Vehicle> valuer) {
int total = 0;
for(Vehicle v : vehicles) {
    total += valuer.evaluate(v);
}
return total;}

メソッドtotalValueはVehiclesをValuerに入れます。拡張範囲はsuperよりはるかに一般的であることを知っておくと便利です。

1
Kevin STS

投票された回答には、さまざまな側面に関する詳細が含まれています。しかし、私はこれとは異なる方法で答えようとします。

考慮すべきことが2つあります。

1.リスト変数への代入

List<? extends X> listvar;

ここでは、listvarに任意のXのリストまたはXのサブクラスのリストを代入することができます _を使用できます。

List<? extends Number> listvar; listvar = new ArrayList<Number>(); listvar = new ArrayList<Integer>();


List<? super X> listvar;

ここで、listvarには、任意のXのリストまたはXのスーパークラスのリストを代入することができます _を使用できます。

List<? super Number> listvar; listvar = new ArrayList<Number>(); listvar = new ArrayList<Object>();

2.リスト変数に対して読み取りまたは書き込み操作を実行します。

`List<? extends X> listvar;`

この機能を使用して、メソッドの引数でリストを受け取り、type Xに対して任意の操作を実行できます(注:listからタイプXのオブジェクトのみを実行できます)。

`List<? super Number> listvar;

この機能を使用して、メソッドの引数でリストを受け取り、リストからタイプObjectのオブジェクトのみを実行できるようにタイプObjectに対して任意の操作を実行できます。 ただし、ここで追加のことは、タイプXのオブジェクトをリストに追加できることです。

1

一般的なワイルドカードは、2つの主なニーズをターゲットにしています。

総称コレクションからの読み取り総称コレクションへの挿入総称ワイルドカードを使用してコレクション(変数)を定義する方法は3つあります。これらは:

List<?>           listUknown = new ArrayList<A>();
List<? extends A> listUknown = new ArrayList<A>();
List<? super   A> listUknown = new ArrayList<A>();

List<?>は未知の型に型付けされたリストを意味します。これはList<A>List<B>List<String>などです。

List<? extends A>は、class A、またはsubclasses of Aのインスタンスであるオブジェクトのリストを意味します(例:BとC)。 List<? super A>は、リストがA classまたはsuperclass of Aのいずれかに入力されることを意味します。

続きを読む: http://tutorials.jenkov.com/Java-generics/wildcards.html

1
Vaibhav Gupta
                                       |
                                       v

リスト<? X> を拡張する は、任意の サブクラス Xまたはそれ自体Xのいずれかです。

リスト<?スーパーX> は、 スーパークラス of X、またはそれ自体Xのいずれかです。

                                      ^
                                      |
0
snr

例、継承の順番はO> S> T> U> Vとします。

extendsキーワードの使用、

正しい:

List<? extends T> Object = new List<T>();
List<? extends T> Object = new List<U>();
List<? extends T> Object = new List<V>();

不正確:

List<? extends T> Object = new List<S>();
List<? extends T> Object = new List<O>();

スーパーキーワード:

正しい:

List<? super T> Object = new List<T>();
List<? super T> Object = new List<S>();
List<? super T> Object = new List<O>();

不正確:

List<? super T> Object = new List<U>();
List<? super T> Object = new List<V>();

オブジェクトを追加します。List Object = new List();

Object.add(new T()); //error

しかし、なぜエラーですか?リストオブジェクトの初期化の可能性を見てみましょう

List<? extends T> Object = new List<T>();
List<? extends T> Object = new List<U>();
List<? extends T> Object = new List<V>();

Object.add(new T())を使用した場合その場合にのみ正しいでしょう

List<? extends T> Object = new List<T>(); 

しかし、さらに2つの可能性があります。

リストオブジェクト= new List();リストオブジェクト= new List();上記の2つの可能性に(new T())を追加しようとすると、TがUとVの上位クラスであるため、エラーが発生します。 Tオブジェクト[(new T())]をU型とV型のListに追加します。上位クラスのオブジェクト(基本クラス)を下位クラスのオブジェクト(下位クラス)に渡すことはできません。

2つの可能性があるため、Javaは参照しているオブジェクトがわからないため、正しい可能性を使用してもエラーが発生します。List Objectにオブジェクトを追加することはできません。有効ではない可能性があるので。

オブジェクトを追加します。List Object = new List();

Object.add(new T()); // compiles fine without error
Object.add(new U()); // compiles fine without error
Object.add(new V()); // compiles fine without error

Object.add(new S()); //  error
Object.add(new O()); //  error

しかし、なぜ上記の2つでエラーが発生するのでしょうか。 Object.add(new T())を使うことができます。以下の可能性についてのみ

List<? super T> Object = new List<T>();
List<? super T> Object = new List<S>();
List<? super T> Object = new List<O>();

List Object = new List()でObject.add(new T())を使用しようとした場合。 List Object = new List()。これはエラーになります。これは、List object = new List()にT object [new T()]を追加できないためです。それはタイプUのオブジェクトだからです。 Tは基底クラスであり、Uはサブクラスであるため、Tオブジェクト[new T()]をUオブジェクトに追加することはできません。基本クラスをサブクラスに追加することはできません。そのため、エラーが発生します。他の場合も同様です。

0
Crox