1047 Stimmen

Unterschied zwischen <? super T> und <? extends T> in Java

Was ist der Unterschied zwischen List<? super T> y List<? extends T> ?

Ich benutzte früher List<? extends T> aber es erlaubt mir nicht, Elemente hinzuzufügen list.add(e) während die List<? super T> tut.

39 Stimmen

Sehen Sie sich diesen ausgezeichneten Vortrag an: youtu.be/V1vQf4qyMXg?t=22m24s

1 Stimmen

Hier null können wir hinzufügen

0 Stimmen

Eine sehr gute Erklärung mit einem Beispiel @ youtube.com/watch?v=34oiEq9nD0M&feature=youtu.be&t=1630, die den <? super T> Teil erklärt, aber eine Idee von einem anderen gibt.

2053voto

Bert F Punkte 81431

extends

Die Wildcard-Deklaration von List<? extends Number> foo3 bedeutet, dass es sich um legale Abtretungen handelt:

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. Lesen - Welche Art von Objekt können Sie angesichts der oben genannten möglichen Zuweisungen garantiert auslesen List foo3 :

    • Sie können eine Number weil jede der Listen, die zugewiesen werden könnten foo3 を含む。 Number oder eine Unterklasse von Number .
    • Sie können nicht lesen Integer denn foo3 könnte auf eine List<Double> .
    • Sie können nicht lesen Double denn foo3 könnte auf eine List<Integer> .
  2. Schreiben - Welche Art von Objekt könnten Sie angesichts der oben genannten möglichen Zuordnungen zu List foo3 die legal wären für 何れも die oben genannten möglichen ArrayList Zuweisungen:

    • Sie können nicht ein Integer denn foo3 könnte auf eine List<Double> .
    • Sie können nicht eine Double denn foo3 könnte auf eine List<Integer> .
    • Sie können nicht eine Number denn foo3 könnte auf eine List<Integer> .

Sie können kein Objekt zu List<? extends T> denn man kann nicht garantieren, welche Art von List auf den es wirklich zeigt, so dass Sie nicht garantieren können, dass das Objekt in diesem Bereich erlaubt ist. List . Die einzige "Garantie" ist, dass Sie nur daraus lesen können und eine T oder Unterklasse von T .

super

Betrachten Sie nun List <? super T> .

Die Wildcard-Deklaration von List<? super Integer> foo3 bedeutet, dass es sich um legale Abtretungen handelt:

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. Lesen - Welche Art von Objekt erhalten Sie angesichts der oben genannten möglichen Zuordnungen garantiert, wenn Sie aus List foo3 :

    • Sie haben keine Garantie für eine Integer denn foo3 könnte auf eine List<Number> o List<Object> .
    • Sie haben keine Garantie für eine Number denn foo3 könnte auf eine List<Object> .
    • En nur Garantie ist, dass Sie eine Instanz eines Object oder Unterklasse von Object (aber Sie wissen nicht, welche Unterklasse).
  2. Schreiben - Welche Art von Objekt könnten Sie angesichts der oben genannten möglichen Zuordnungen zu List foo3 die legal wären für 何れも die oben genannten möglichen ArrayList Zuweisungen:

    • Sie können eine Integer weil ein Integer ist in jeder der oben genannten Listen erlaubt.
    • Sie können eine Instanz einer Unterklasse von Integer weil eine Instanz einer Unterklasse von Integer ist in jeder der oben genannten Listen zulässig.
    • Sie können nicht eine Double denn foo3 könnte auf eine ArrayList<Integer> .
    • Sie können nicht eine Number denn foo3 könnte auf eine ArrayList<Integer> .
    • Sie können nicht ein Object denn foo3 könnte auf eine ArrayList<Integer> .

PECS

Erinnern Sie sich an PECS : "Produzent erweitert, Konsument super" .

  • "Produzent verlängert" - Wenn Sie eine List zu produzieren T Werte (Sie wollen lesen T s aus der Liste), müssen Sie es mit ? extends T z.B. List<? extends Integer> . Aber Sie können dieser Liste nichts hinzufügen.

  • "Consumer Super" - Wenn Sie eine List zu konsumieren T Werte (Sie wollen schreiben T s in die Liste), müssen Sie sie mit ? super T z.B. List<? super Integer> . Es gibt jedoch keine Garantie, welche Art von Objekt Sie aus dieser Liste lesen können.

  • Wenn Sie sowohl von einer Liste lesen als auch in sie schreiben müssen, müssen Sie sie genau und ohne Platzhalter deklarieren, z. B. List<Integer> .

Beispiel

Note dieses Beispiel aus den Java Generics FAQ . Beachten Sie, dass das Quellenverzeichnis src (die erzeugende Liste) verwendet extends und die Zielliste dest (die verbrauchende Liste) verwendet 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)); 
  } 
}

Siehe auch Wie kann ich zu List<? extends Number> Datenstrukturen hinzufügen?

337voto

Luigi Cortese Punkte 10221

Stellen Sie sich diese Hierarchie vor

enter image description here

1. Erweitert

Durch Schreiben

    List<? extends C2> list;

Sie sagen also, dass list in der Lage sein, auf ein Objekt des Typs (zum Beispiel) ArrayList deren generischer Typ einer der 7 Untertypen de C2 ( C2 enthalten):

  1. C2: new ArrayList<C2>(); , (ein Objekt, das C2 oder Subtypen speichern kann) oder
  2. D1: new ArrayList<D1>(); (ein Objekt, das D1 oder Subtypen speichern kann) oder
  3. D2: new ArrayList<D2>(); , (ein Objekt, das D2 oder Subtypen speichern kann) oder...

und so weiter. Sieben verschiedene Fälle:

    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             

Für jeden möglichen Fall gibt es einen Satz von "speicherbaren" Typen: 7 (rote) Sätze, hier grafisch dargestellt

enter image description here

Wie Sie sehen können, gibt es keine sichere Art die allen Fällen gemeinsam ist:

  • können Sie nicht list.add(new C2(){}); denn es könnte sein list = new ArrayList<D1>();
  • können Sie nicht list.add(new D1(){}); denn es könnte sein list = new ArrayList<D2>();

und so weiter.

2. Super

Durch Schreiben

    List<? super C2> list;

Sie sagen also, dass list in der Lage sein, auf ein Objekt des Typs (zum Beispiel) ArrayList deren generischer Typ einer der 7 Supertypen de C2 ( C2 enthalten):

  • A1: new ArrayList<A1>(); (ein Objekt, das A1 oder Subtypen speichern kann) oder
  • A2: new ArrayList<A2>(); (ein Objekt, das A2 oder Subtypen speichern kann) oder
  • A3: new ArrayList<A3>(); , (ein Objekt, das A3 oder Subtypen speichern kann) oder...

und so weiter. Sieben verschiedene Fälle:

    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

Wir haben eine Reihe von "speicherbaren" Typen für jeden möglichen Fall: 7 (rote) Sätze, hier grafisch dargestellt

enter image description here

Wie Sie sehen können, haben wir hier sieben sichere Arten die allen Fällen gemeinsam sind: C2 , D1 , D2 , E1 , E2 , E3 , E4 .

  • können Sie list.add(new C2(){}); denn unabhängig von der Art der Liste, auf die wir uns beziehen, C2 ist erlaubt
  • können Sie list.add(new D1(){}); denn unabhängig von der Art der Liste, auf die wir uns beziehen, D1 ist erlaubt

und so weiter. Sie haben wahrscheinlich bemerkt, dass diese Typen der Hierarchie entsprechen, die mit dem Typ C2 .

Anmerkungen

Hier die vollständige Hierarchie, wenn Sie einige Tests durchführen möchten

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{}

161voto

Michael Dausmann Punkte 3635

Ich liebe die Antwort von @Bert F., aber mein Gehirn sieht es so.

Ich habe ein X in meiner Hand. Wenn ich möchte, dass schreiben. meine X in eine Liste zu übertragen, muss diese Liste entweder eine Liste von X oder eine Liste von Dingen sein, in die meine X umgewandelt werden können, wenn ich sie eintrage, d. h. jede Oberklasse von X...

List<? super   X>

Wenn ich eine Liste bekomme und ich möchte lesen ein X aus dieser Liste, dann ist das besser eine Liste von X oder eine Liste von Dingen, die zu X aufgewertet werden können, wenn ich sie vorlese, d.h. alles, was erweitert X

List<? extends X>

Ich hoffe, das hilft.

47voto

Oleksandr Pyrohov Punkte 13894

Ich würde den Unterschied gerne visualisieren. Nehmen wir an, wir haben:

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

List<? extends T> - Lesen und Zuordnen:

|-------------------------|-------------------|---------------------------------|
|         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> - Schreiben und Zuweisen:

|-------------------------|-------------------|-------------------------------------------|
|         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>                    |
|-------------------------|-------------------|-------------------------------------------|

In allen Fällen:

  • können Sie jederzeit erhalten. Object aus einer Liste unabhängig vom Platzhalter.

  • können Sie jederzeit hinzufügen. null zu einer veränderbaren Liste, unabhängig vom Platzhalter.

34voto

Sushant Punkte 1814

Basierend auf Bert F's Antwort Ich möchte mein Verständnis erläutern.

Sagen wir, wir haben 3 Klassen als

public class Fruit{}

public class Melon extends Fruit{}

public class WaterMelon extends Melon{}

Hier haben wir

List<? extends Fruit> fruitExtendedList = …

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

Ok, jetzt versuchen wir, einen Wert von fruitExtendedList zu erhalten

Fruit fruit = fruitExtendedList.get(position)

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

Versuchen wir es noch einmal

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.

Das Gleiche gilt für

WaterMelon waterMelon = fruitExtendedList.get(position)

Versuchen wir nun, ein Objekt in fruitExtendedList zu setzen

Hinzufügen des Objekts Obst

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.

Hinzufügen des Objekts Melone

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.

Schließlich versuchen wir, das WaterMelon-Objekt hinzuzufügen

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.

Doch halt was wäre, wenn jemand beschließt, eine neue Art von Zitrone zu schaffen, sagen wir mal, um der Argumente willen SaltyLemon als

public class SaltyLemon extends Lemon{}

Jetzt kann fruitExtendedList eine Liste von Fruit, Melon, WaterMelon oder SaltyLemon sein.

Also, unsere Aussage

fruitExtendedList.add(new WaterMelon())

ist ebenfalls nicht gültig.

Grundsätzlich können wir sagen, dass wir nichts in eine fruitExtendedList schreiben können.

Dies fasst zusammen List<? extends Fruit>

Jetzt sehen wir

List<? super Melon> melonSuperList= …

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

Versuchen wir nun, einen Wert aus melonSuperList zu erhalten

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

Ebenso kann Melon, WaterMelon oder ein anderes Objekt nicht gelesen werden.

Beachten Sie aber, dass wir Instanzen des Typs Object lesen können

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.

Versuchen wir nun, einen Wert aus melonSuperList zu setzen.

Hinzufügen des Objekttyps Objekt

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.

Hinzufügen eines Objekts vom Typ Obst

melonSuperList.add(new Fruit())

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

Hinzufügen eines Objekts vom Typ Melone

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.

Hinzufügen eines Objekts vom Typ WaterMelon

melonSuperList.add(new WaterMelon())

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

Zusammenfassend können wir Melon oder seine Unterklasse in melonSuperList hinzufügen und nur Objekte vom Typ Object lesen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X