CodeGym /Blog Java /Aleatoriu /Utilizarea varargs atunci când lucrați cu generice
John Squirrels
Nivel
San Francisco

Utilizarea varargs atunci când lucrați cu generice

Publicat în grup
Bună! În lecția de astăzi, vom continua să studiem genericele. După cum se întâmplă, acesta este un subiect mare, dar nu trebuie evitat - este o parte extrem de importantă a limbajului :) Când studiați documentația Oracle despre generice sau citiți tutoriale online, veți întâlni termenii tipuri nereificabile și tipuri fiabile . Un tip confirmabil este un tip pentru care informațiile sunt complet disponibile în timpul execuției. În Java, astfel de tipuri includ primitive, tipuri brute și tipuri negenerice. În schimb, tipurile nereificabile sunt tipuri ale căror informații sunt șterse și devin inaccesibile în timpul execuției. După cum se întâmplă, acestea sunt generice — List<String>, List<Integer>etc.

Apropo, vă amintiți ce este varargs?

În cazul în care ați uitat, acesta este un argument de lungime variabilă. Sunt utile în situațiile în care nu știm câte argumente ar putea fi transmise metodei noastre. De exemplu, dacă avem o clasă de calculator care are o summetodă. Metoda sum()poate primi 2 numere, sau 3, sau 5, sau câte doriți. Ar fi foarte ciudat să supraîncărcați sum()metoda pentru fiecare număr posibil de argumente. În schimb, putem face acest lucru:

public class SimpleCalculator {

   public static int sum(int...numbers) {

       int result = 0;

       for(int i : numbers) {

           result += i;
       }

       return result;
   }

   public static void main(String[] args) {

       System.out.println(sum(1,2,3,4,5));
       System.out.println(sum(2,9));
   }
}
Ieșire din consolă:

15
11
Acest lucru ne arată că există câteva caracteristici importante atunci când folosiți varargs în combinație cu generice. Să ne uităm la următorul cod:

import javafx.util.Pair;
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static <E> void addAll(List<E> list, E... array) {

       for (E element : array) {
           list.add(element);
       }
   }

   public static void main(String[] args) {
       addAll(new ArrayList<String>(), // This is okay
               "Leonardo da Vinci",
               "Vasco de Gama"
       );

       // but here we get a warning
       addAll(new ArrayList<Pair<String, String>>(),
               new Pair<String, String>("Leonardo", "da Vinci"),
               new Pair<String, String>("Vasco", "de Gama")
       );
   }
}
Metoda addAll()ia ca intrare a List<E>și orice număr de Eobiecte, apoi adaugă toate aceste obiecte în listă. În main()metodă, numim addAll()metoda noastră de două ori. În primul caz, adăugăm două șiruri obișnuite la List. Totul este în ordine aici. În al doilea caz, adăugăm două Pair<String, String>obiecte la List. Dar aici primim în mod neașteptat un avertisment:

Unchecked generics array creation for varargs parameter
Ce înseamnă asta? De ce primim un avertisment și de ce există vreo mențiune despre un array? La urma urmei, codul nostru nu are un array! Să începem cu al doilea caz. Avertismentul menționează o matrice deoarece compilatorul convertește argumentul cu lungime variabilă (varargs) într-o matrice. Cu alte cuvinte, semnătura addAll()metodei noastre este:

public static <E> void addAll(List<E> list, E... array)
De fapt, arată așa:

public static <E> void addAll(List<E> list, E[] array)
Adică, în main()metodă, compilatorul convertește codul nostru în acesta:

public static void main(String[] args) { 
   addAll(new ArrayList<String>(), 
      new String[] { 
        "Leonardo da Vinci", 
        "Vasco de Gama" 
      } 
   ); 
   addAll(new ArrayList<Pair<String,String>>(),
        new Pair<String,String>[] { 
            new Pair<String,String>("Leonardo","da Vinci"), 
            new Pair<String,String>("Vasco","de Gama") 
        } 
   ); 
}
O Stringmatrice este bine. Dar o Pair<String, String>matrice nu este. Problema este că Pair<String, String>este un tip nerefiabil. În timpul compilării, toate informațiile despre argumentele de tip (<String, String>) sunt șterse. Crearea de tablouri de tip nereificabil nu este permisă în Java . Puteți vedea acest lucru dacă încercați să creați manual o matrice Pair<String, String>

public static void main(String[] args) {

   // Compilation error Generic array creation
  Pair<String, String>[] array = new Pair<String, String>[10];
}
Motivul este evident: tip siguranța. După cum vă veți aminti, atunci când creați o matrice, trebuie neapărat să specificați ce obiecte (sau primitive) va stoca matricea.

int array[] = new int[10];
Într-una dintre lecțiile noastre anterioare, am examinat ștergerea tipului în detaliu. În acest caz, ștergerea tipului ne face să pierdem informațiile pe care Pairobiectele le stochează <String, String>perechi. Crearea matricei ar fi nesigură. Când utilizați metode care implică varargs și generice, asigurați-vă că vă amintiți despre ștergerea tipului și cum funcționează. Dacă sunteți absolut sigur de codul pe care l-ați scris și știți că nu va cauza probleme, puteți dezactiva avertismentele legate de varargs folosind adnotări @SafeVarargs.

@SafeVarargs
public static <E> void addAll(List<E> list, E... array) {

   for (E element : array) {
       list.add(element);
   }
}
Dacă adăugați această adnotare la metoda dvs., avertismentul pe care l-am întâlnit mai devreme nu va apărea. O altă problemă care poate apărea la utilizarea vararg-urilor cu generice este poluarea în grămada. Utilizarea varargs atunci când lucrați cu generice - 3Poluarea în grămada se poate produce în următoarele situații:

import java.util.ArrayList;
import java.util.List;

public class Main {

   static List<String> polluteHeap() {
       List numbers = new ArrayList<Number>();
       numbers.add(1);
       List<String> strings = numbers;
       strings.add("");
       return strings;
   }

   public static void main(String[] args) {

       List<String> stringsWithHeapPollution = polluteHeap();

       System.out.println(stringsWithHeapPollution.get(0));
   }
}
Ieșire din consolă:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
În termeni simpli, poluarea în grămada este atunci când obiectele de tip Aar trebui să fie în grămada, dar obiectele de tip Bajung acolo din cauza erorilor legate de siguranța tipului. În exemplul nostru, exact asta se întâmplă. Mai întâi, am creat numbersvariabila brută și i-am atribuit o colecție generică ( ArrayList<Number>). Apoi am adăugat numărul 1la colecție.

List<String> strings = numbers;
Pe această linie, compilatorul a încercat să ne avertizeze despre posibile erori prin lansarea avertismentului „ Atribuire neverificată... ”, dar am ignorat-o. Sfârșim cu o variabilă generică de tip List<String>care indică o colecție generică de tip ArrayList<Number>. În mod clar, această situație poate duce la probleme! Și așa se întâmplă. Folosind noua noastră variabilă, adăugăm un șir la colecție. Acum avem poluare în grămada - am adăugat un număr și apoi un șir la colecția parametrizată. Compilatorul ne-a avertizat, dar i-am ignorat avertismentul. Ca rezultat, primim ClassCastExceptiondoar un timp în care programul rulează. Deci, ce legătură are asta cu varargii? Utilizarea vararg-urilor cu generice poate duce cu ușurință la poluare în grămada. Iată un exemplu simplu:

import java.util.Arrays;
import java.util.List;

public class Main {

   static void polluteHeap(List<String>... stringsLists) {
       Object[] array = stringsLists;
       List<Integer> numbersList = Arrays.asList(66,22,44,12);

       array[0] = numbersList;
       String str = stringsLists[0].get(0);
   }

   public static void main(String[] args) {

       List<String> cars1 = Arrays.asList("Ford", "Fiat", "Kia");
       List<String> cars2 = Arrays.asList("Ferrari", "Bugatti", "Zaporozhets");

       polluteHeap(cars1, cars2);
   }
}
Ce se petrece aici? Datorită ștergerii tipului, argumentul nostru cu lungime variabilă

List<String>...stringsLists
devine un tablou de liste, adică List[], de obiecte de tip necunoscut (nu uitați că varargs se transformă într-un tablou obișnuit în timpul compilării). Din acest motiv, îl putem atribui cu ușurință variabilei Object[] arraydin prima linie a metodei - tipul obiectelor din listele noastre a fost șters! Și acum avem o Object[]variabilă, la care putem adăuga orice, deoarece toate obiectele din Java moștenesc Object! La început, avem doar o serie de liste de șiruri. Dar datorită ștergerii tipului și folosirii vararg-urilor, putem adăuga cu ușurință o listă de numere, ceea ce facem. Drept urmare, poluăm grămada amestecând obiecte de diferite tipuri. Rezultatul va fi încă unul ClassCastExceptioncând vom încerca să citim un șir din matrice. Ieșire din consolă:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Astfel de consecințe neașteptate pot fi cauzate de folosirea vararg-urilor, un mecanism aparent simplu :) Și odată cu asta, lecția de astăzi se încheie. Nu uitați să rezolvați câteva sarcini și, dacă aveți timp și energie, studiați câteva lecturi suplimentare. „ Java eficient ” nu se va citi de la sine! :) Pana data viitoare!
Comentarii
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION