CodeGym /Java blog /Tilfældig /Brug af varargs, når du arbejder med generiske lægemidler...
John Squirrels
Niveau
San Francisco

Brug af varargs, når du arbejder med generiske lægemidler

Udgivet i gruppen
Hej! I dagens lektion vil vi fortsætte med at studere generika. Som det sker, er dette et stort emne, men det er ikke til at komme udenom – det er en ekstremt vigtig del af sproget :) Når du studerer Oracle-dokumentationen om generiske lægemidler eller læser online tutorials, vil du støde på begreberne ikke-tilbageførbare typer og reificerbare typer . En reificerbar type er en type, for hvilken information er fuldt tilgængelig under kørsel. I Java inkluderer sådanne typer primitiver, råtyper og ikke-generiske typer. I modsætning hertil er typer, der ikke kan tilbagebetales, typer, hvis oplysninger slettes og bliver utilgængelige under kørsel. Som det sker, er disse generiske lægemidler - List<String>, List<Integer>, osv.

Kan du forresten huske hvad varargs er?

Hvis du har glemt det, er dette et argument med variabel længde. De er nyttige i situationer, hvor vi ikke ved, hvor mange argumenter der kan overføres til vores metode. For eksempel hvis vi har en lommeregnerklasse, der har en summetode. Metoden sum()kan modtage 2 numre, eller 3, eller 5, eller så mange du vil. Det ville være meget mærkeligt at overbelaste sum()metoden for ethvert muligt antal argumenter. I stedet kan vi gø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));
   }
}
Konsoludgang:

15
11
Dette viser os, at der er nogle vigtige funktioner, når du bruger varargs i kombination med generiske lægemidler. Lad os 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()tager som input et List<E>og et hvilket som helst antal Eobjekter, og derefter tilføjer den alle disse objekter til listen. I main()metoden kalder vi vores addAll()metode to gange. I det første tilfælde tilføjer vi to almindelige strenge til List. Alt er i orden her. I det andet tilfælde tilføjer vi to Pair<String, String>objekter til List. Men her får vi uventet en advarsel:

Unchecked generics array creation for varargs parameter
Hvad betyder det? Hvorfor får vi en advarsel, og hvorfor er der nogen omtale af en array? Vores kode har trods alt ikke et array! Lad os starte med det andet tilfælde. Advarslen nævner et array, fordi compileren konverterer argumentet med variabel længde (varargs) til et array. addAll()Med andre ord er signaturen på vores metode:

public static <E> void addAll(List<E> list, E... array)
Det ser faktisk sådan ud:

public static <E> void addAll(List<E> list, E[] array)
Det vil sige, i main()metoden konverterer compileren vores 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") 
        } 
   ); 
}
Et Stringarray er fint. Men et Pair<String, String>array er det ikke. Problemet er, at det Pair<String, String>er en type, der ikke kan tilbagebetales. Under kompilering slettes al information om typeargumenter (<String, String>). Oprettelse af arrays af en type, der ikke kan revideres, er ikke tilladt i Java . Du kan se dette, hvis du forsøger manuelt at oprette et Pair<String, String>-array

public static void main(String[] args) {

   // Compilation error Generic array creation
  Pair<String, String>[] array = new Pair<String, String>[10];
}
Årsagen er indlysende: typesikkerhed. Som du vil huske, skal du, når du opretter et array, bestemt specificere hvilke objekter (eller primitiver) arrayet vil gemme.

int array[] = new int[10];
I en af ​​vores tidligere lektioner undersøgte vi typesletning i detaljer. I dette tilfælde får type sletning os til at miste den information, som objekterne Pairlagrer <String, String>par. At oprette arrayet ville være usikkert. Når du bruger metoder, der involverer varargs og generiske lægemidler, skal du huske at slette typen, og hvordan det virker. Hvis du er helt sikker på den kode, du har skrevet, og ved, at den ikke vil forårsage nogen problemer, kan du slå de varargs-relaterede advarsler fra ved hjælp af annoteringerne @SafeVarargs.

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

   for (E element : array) {
       list.add(element);
   }
}
Hvis du tilføjer denne anmærkning til din metode, vises den advarsel, vi stødte på tidligere, ikke. Et andet problem, der kan opstå, når du bruger varargs med generiske lægemidler, er heap-forurening. Brug af varargs, når du arbejder med generiske lægemidler - 3Dyngeforurening kan ske i følgende situation:

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

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Enkelt sagt er dyngeforurening, når objekter af typen Askal være i dyngen, men objekter af typen Bender der på grund af fejl relateret til typesikkerhed. I vores eksempel er det præcis, hvad der sker. Først oprettede vi den rå numbersvariabel og tildelte ArrayList<Number>den en generisk samling ( ). Så føjede vi nummeret 1til samlingen.

List<String> strings = numbers;
På denne linje forsøgte compileren at advare os om mulige fejl ved at udsende advarslen " Ukontrolleret tildeling... ", men vi ignorerede den. Vi ender med en generisk variabel af typen List<String>, der peger på en generisk samling af typen ArrayList<Number>. Det er klart, at denne situation kan føre til problemer! Og det gør det. Ved at bruge vores nye variabel tilføjer vi en streng til samlingen. Vi har nu heap-forurening - vi tilføjede et tal og derefter en streng til den parametriserede samling. Compileren advarede os, men vi ignorerede dens advarsel. Som et resultat får vi en ClassCastExceptionkun, mens programmet kører. Så hvad har det med varargs at gøre? Brug af varargs med generiske lægemidler kan nemt føre til heap-forurening. Her er et simpelt 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);
   }
}
Hvad sker der her? På grund af type sletning, vores variabel-længde argument

List<String>...stringsLists
bliver en matrix af lister, dvs. List[]af objekter af en ukendt type (glem ikke, at varargs bliver til en regulær matrix under kompilering). På grund af dette kan vi nemt tildele den til variablen Object[] arrayi metodens første linje - typen af ​​objekter i vores lister er blevet slettet! Og nu har vi en Object[]variabel, som vi overhovedet kan tilføje hvad som helst, da alle objekter i Java arver Object! I starten har vi kun en række lister over strenge. Men takket være tekstsletning og vores brug af varargs, kan vi nemt tilføje en liste med tal, hvilket vi gør. Som et resultat forurener vi dyngen ved at blande genstande af forskellige typer. Resultatet bliver endnu et ClassCastException, når vi forsøger at læse en streng fra arrayet. Konsoludgang:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Sådanne uventede konsekvenser kan forårsages ved at bruge varargs, en tilsyneladende simpel mekanisme :) Og dermed slutter dagens lektion. Glem ikke at løse et par opgaver, og hvis du har tid og energi, så læs lidt ekstra læsning. " Effektiv Java " vil ikke læse sig selv! :) Indtil næste gang!
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION