CodeGym /Java-blogg /Tilfeldig /Bruke varargs når du arbeider med generiske legemidler
John Squirrels
Nivå
San Francisco

Bruke varargs når du arbeider med generiske legemidler

Publisert i gruppen
Hei! I dagens leksjon vil vi fortsette å studere generikk. Som det skjer, er dette et stort emne, men det er ikke til å unngå det – det er en ekstremt viktig del av språket :) Når du studerer Oracle-dokumentasjonen om generiske medisiner eller leser online veiledninger, vil du komme over begrepene ikke-refiable typer og reifiserbare typer . En reifiserbar type er en type som informasjon er fullt tilgjengelig for under kjøring. I Java inkluderer slike typer primitiver, råtyper og ikke-generiske typer. I motsetning til dette er ikke-reifiable typer typer hvis informasjon blir slettet og blir utilgjengelig under kjøring. Som det skjer, er dette generiske legemidler - List<String>, List<Integer>, etc.

Husker du forresten hva varargs er?

I tilfelle du har glemt det, er dette et argument med variabel lengde. De er nyttige i situasjoner der vi ikke vet hvor mange argumenter som kan overføres til metoden vår. For eksempel hvis vi har en kalkulatorklasse som har en summetode. Metoden sum()kan motta 2 tall, eller 3, eller 5, eller så mange du vil. Det ville være veldig rart å overbelaste sum()metoden for hvert mulig antall argumenter. I stedet kan vi gjøre dette:

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));
   }
}
Konsoll utgang:

15
11
Dette viser oss at det er noen viktige funksjoner når du bruker varargs i kombinasjon med generika. La oss se på følgende kode:

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")
       );
   }
}
Metoden addAll()tar som input et List<E>og et hvilket som helst antall Eobjekter, og så legger den alle disse objektene til listen. I main()metoden kaller vi addAll()metoden vår to ganger. I det første tilfellet legger vi til to vanlige strenger til List. Alt er i orden her. I det andre tilfellet legger vi til to Pair<String, String>objekter til List. Men her får vi uventet en advarsel:

Unchecked generics array creation for varargs parameter
Hva betyr det? Hvorfor får vi en advarsel og hvorfor er det noen omtale av en array? Tross alt har ikke koden vår en array! La oss starte med det andre tilfellet. Advarselen nevner en matrise fordi kompilatoren konverterer argumentet med variabel lengde (varargs) til en matrise. Med andre ord, signaturen til addAll()metoden vår er:

public static <E> void addAll(List<E> list, E... array)
Det ser faktisk slik ut:

public static <E> void addAll(List<E> list, E[] array)
Det vil si at i main()metoden konverterer kompilatoren vår kode til dette:

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") 
        } 
   ); 
}
En Stringarray er helt greit. Men en Pair<String, String>matrise er det ikke. Problemet er at det Pair<String, String>er en ikke-reifiable type. Under kompilering blir all informasjon om typeargumenter (<String, String>) slettet. Det er ikke tillatt å lage arrays av en ikke-reifiable type i Java . Du kan se dette hvis du prøver å manuelt lage en Pair<String, String>-matrise

public static void main(String[] args) {

   // Compilation error Generic array creation
  Pair<String, String>[] array = new Pair<String, String>[10];
}
Årsaken er åpenbar: typesikkerhet. Som du vil huske, når du oppretter en matrise, må du definitivt spesifisere hvilke objekter (eller primitiver) matrisen skal lagre.

int array[] = new int[10];
I en av våre tidligere leksjoner undersøkte vi typesletting i detalj. I dette tilfellet fører type sletting til at vi mister informasjonen som objektene Pairlagrer <String, String>parene. Å lage matrisen ville være utrygt. Når du bruker metoder som involverer varargs og generiske medisiner, husk å huske om type sletting og hvordan det fungerer. Hvis du er helt sikker på koden du har skrevet og vet at den ikke vil forårsake noen problemer, kan du slå av varargs-relaterte advarsler ved å bruke merknadene @SafeVarargs.

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

   for (E element : array) {
       list.add(element);
   }
}
Hvis du legger til denne merknaden i metoden din, vises ikke advarselen vi møtte tidligere. Et annet problem som kan oppstå ved bruk av varargs med generiske legemidler, er haugforurensning. Bruke varargs når du arbeider med generiske medisiner - 3Heapforurensning kan skje i følgende situasjon:

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));
   }
}
Konsoll utgang:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Enkelt sagt er haugforurensning når objekter av type Askal være i haugen, men objekter av type Bhavner der på grunn av feil knyttet til typesikkerhet. I vårt eksempel er det nettopp dette som skjer. Først opprettet vi råvariabelen numbersog tilordnet en generisk samling ( ArrayList<Number>) til den. Så la vi nummeret 1til samlingen.

List<String> strings = numbers;
På denne linjen prøvde kompilatoren å advare oss om mulige feil ved å utstede advarselen " Ukontrollert oppdrag... ", men vi ignorerte den. Vi ender opp med en generisk variabel av typen List<String>som peker til en generisk samling av typen ArrayList<Number>. Det er klart at denne situasjonen kan føre til trøbbel! Og det gjør det. Ved å bruke vår nye variabel legger vi til en streng i samlingen. Vi har nå haugforurensning - vi la til et tall og deretter en streng til den parametriserte samlingen. Kompilatoren advarte oss, men vi ignorerte advarselen. Som et resultat får vi en ClassCastExceptionbare mens programmet kjører. Så hva har dette med varargs å gjøre? Bruk av varargs med generiske legemidler kan lett føre til haugforurensning. Her er et enkelt eksempel:

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);
   }
}
Hva foregår her? På grunn av type sletting, vårt variabellengde-argument

List<String>...stringsLists
blir en rekke lister, dvs. List[]objekter av ukjent type (ikke glem at varargs blir til en vanlig matrise under kompilering). På grunn av dette kan vi enkelt tilordne den til Object[] arrayvariabelen i den første linjen i metoden - typen objekter i listene våre er slettet! Og nå har vi en Object[]variabel som vi i det hele tatt kan legge til, siden alle objekter i Java arver Object! Til å begynne med har vi bare en rekke lister med strenger. Men takket være tekstsletting og vår bruk av varargs, kan vi enkelt legge til en liste med tall, noe vi gjør. Som et resultat forurenser vi haugen ved å blande gjenstander av forskjellige typer. Resultatet blir enda et ClassCastExceptionnår vi prøver å lese en streng fra arrayet. Konsoll utgang:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Slike uventede konsekvenser kan forårsakes av å bruke varargs, en tilsynelatende enkel mekanisme :) Og med det slutter dagens leksjon. Ikke glem å løse et par oppgaver, og hvis du har tid og energi, studer litt ekstra lesing. " Effektiv Java " vil ikke lese seg selv! :) Til neste gang!
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION