Java >> Java tutorial >  >> Tag >> extends

Hvordan kan jeg tilføje til List<? udvider Number> datastrukturer?

Beklager, men du kan ikke.

Wildcard-erklæringen List<? extends Number> foo3 betyder, at variablen foo3 kan indeholde enhver værdi fra en familie af typer (i stedet for enhver værdi af en bestemt type). Det betyder, at enhver af disse er juridiske opgaver:

List<? extends Number> foo3 = new ArrayList<Number>();  // Number "extends" Number
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>();  // Double extends Number

Så givet dette, hvilken type objekt kan du tilføje til List foo3 det ville være lovligt efter enhver af ovenstående mulige ArrayList opgaver:

  • Du kan ikke tilføje en Integer fordi foo3 kunne pege på en List<Double> .
  • Du kan ikke tilføje en Double fordi foo3 kunne pege på en List<Integer> .
  • Du kan ikke tilføje en Number fordi foo3 kunne pege på en List<Integer> .

Du kan ikke tilføje noget objekt til List<? extends T> fordi du ikke kan garantere, hvilken slags List det peger virkelig på, så du kan ikke garantere, at objektet er tilladt i den List . Den eneste "garanti" er, at du kun kan læse fra den, og du får en T eller underklasse af T .

Den omvendte logik gælder for super , for eksempel. List<? super T> . Disse er lovlige:

List<? super Number> foo3 = new ArrayList<Number>(); // Number is a "super" of Number
List<? super Number> foo3 = new ArrayList<Object>(); // Object is a "super" of Number

Du kan ikke læse den specifikke type T (f.eks. Number ) fra List<? super T> fordi du ikke kan garantere, hvilken slags List det peger virkelig på. Den eneste "garanti" du har, er at du kan tilføje en værdi af typen T (eller enhver underklasse af T ) uden at krænke integriteten af ​​den liste, der peges på.

Det perfekte eksempel på dette er signaturen for Collections.copy() :

public static <T> void copy(List<? super T> dest, List<? extends T> src)

Bemærk, hvordan src listeerklæring bruger extends for at tillade mig at sende en liste fra en familie af relaterede listetyper og stadig garantere, at den vil producere værdier af type T eller underklasser af T. Men du kan ikke tilføje til src liste.

dest listeerklæring bruger super for at give mig mulighed for at videregive enhver liste fra en familie af relaterede listetyper og stadig garantere, at jeg kan skrive en værdi af en specifik type T til den liste. Men det kan ikke garanteres at læse værdierne for specifik skriv T, hvis jeg læser fra listen.

Så nu, takket være generiske jokertegn, kan jeg foretage alle disse opkald med den enkelte metode:

// copy(dest, src)
Collections.copy(new ArrayList<Number>(), new ArrayList<Number>());
Collections.copy(new ArrayList<Number>(), new ArrayList<Integer>());
Collections.copy(new ArrayList<Object>(), new ArrayList<Number>());
Collections.copy(new ArrayList<Object>(), new ArrayList<Double>());

Overvej denne forvirrende og meget brede kode til at træne din hjerne. De kommenterede linjer er ulovlige, og årsagen er angivet yderst til højre for linjen (skal scrolle for at se nogle af dem):

  List<Number> listNumber_ListNumber  = new ArrayList<Number>();
//List<Number> listNumber_ListInteger = new ArrayList<Integer>();                    // error - can assign only exactly <Number>
//List<Number> listNumber_ListDouble  = new ArrayList<Double>();                     // error - can assign only exactly <Number>
  
  List<? extends Number> listExtendsNumber_ListNumber  = new ArrayList<Number>();
  List<? extends Number> listExtendsNumber_ListInteger = new ArrayList<Integer>();
  List<? extends Number> listExtendsNumber_ListDouble  = new ArrayList<Double>();
  
  List<? super Number> listSuperNumber_ListNumber  = new ArrayList<Number>();
//List<? super Number> listSuperNumber_ListInteger = new ArrayList<Integer>();      // error - Integer is not superclass of Number
//List<? super Number> listSuperNumber_ListDouble  = new ArrayList<Double>();       // error - Double is not superclass of Number
  

//List<Integer> listInteger_ListNumber  = new ArrayList<Number>();                  // error - can assign only exactly <Integer>
  List<Integer> listInteger_ListInteger = new ArrayList<Integer>();
//List<Integer> listInteger_ListDouble  = new ArrayList<Double>();                  // error - can assign only exactly <Integer>
  
//List<? extends Integer> listExtendsInteger_ListNumber  = new ArrayList<Number>(); // error - Number is not a subclass of Integer
  List<? extends Integer> listExtendsInteger_ListInteger = new ArrayList<Integer>();
//List<? extends Integer> listExtendsInteger_ListDouble  = new ArrayList<Double>(); // error - Double is not a subclass of Integer
  
  List<? super Integer> listSuperInteger_ListNumber  = new ArrayList<Number>();
  List<? super Integer> listSuperInteger_ListInteger = new ArrayList<Integer>();
//List<? super Integer> listSuperInteger_ListDouble  = new ArrayList<Double>();     // error - Double is not a superclass of Integer


  listNumber_ListNumber.add(3);             // ok - allowed to add Integer to exactly List<Number>
  
  // These next 3 are compile errors for the same reason:
  // You don't know what kind of List<T> is really
  // being referenced - it may not be able to hold an Integer.
  // You can't add anything (not Object, Number, Integer,
  // nor Double) to List<? extends Number>      
//listExtendsNumber_ListNumber.add(3);     // error - can't add Integer to *possible* List<Double>, even though it is really List<Number>
//listExtendsNumber_ListInteger.add(3);    // error - can't add Integer to *possible* List<Double>, even though it is really List<Integer>
//listExtendsNumber_ListDouble.add(3);     // error - can't add Integer to *possible* List<Double>, especially since it is really List<Double>

  listSuperNumber_ListNumber.add(3);       // ok - allowed to add Integer to List<Number> or List<Object>
  
  listInteger_ListInteger.add(3);          // ok - allowed to add Integer to exactly List<Integer> (duh)

  // This fails for same reason above - you can't
  // guarantee what kind of List the var is really
  // pointing to
//listExtendsInteger_ListInteger.add(3);   // error - can't add Integer to *possible* List<X> that is only allowed to hold X's
  
  listSuperInteger_ListNumber.add(3);      // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object>
  listSuperInteger_ListInteger.add(3);     // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object>

Det kan du ikke (uden usikre casts). Du kan kun læse fra dem.

Problemet er, at du ikke ved, hvad listen præcis er en liste over. Det kan være en liste over en hvilken som helst underklasse af Number, så når du forsøger at sætte et element ind i det, ved du ikke, om elementet faktisk passer ind i listen.

For eksempel kan listen være en liste med Byte s, så det ville være en fejl at sætte en Float ind i det.


"Liste '<' ? udvider nummer> er faktisk et jokertegn for øvre grænse !

Jokertegnet med øvre grænse siger, at enhver klasse, der udvider Number eller Number selv, kan bruges som den formelle parametertype:Problemet stammer fra det faktum, at Java ved ikke, hvilken type List virkelig er. Det skal være en PRÆCIS og UNIK Type. Jeg håber det hjælper :)


Java tag