CodeGym /Java blogg /Slumpmässig /Att använda varargs när man arbetar med generika
John Squirrels
Nivå
San Francisco

Att använda varargs när man arbetar med generika

Publicerad i gruppen
Hej! I dagens lektion kommer vi att fortsätta studera generika. Som det råkar är det här ett stort ämne, men det går inte att undvika det — det är en extremt viktig del av språket :) När du studerar Oracle-dokumentationen om generika eller läser online-tutorials kommer du att stöta på termerna icke-återförbara typer och reifierbara typer . En reifierbar typ är en typ för vilken information är fullt tillgänglig vid körning. I Java inkluderar sådana typer primitiver, råtyper och icke-generiska typer. Däremot är icke-återställbara typer typer vars information raderas och blir otillgängliga under körning. Som det händer är dessa generika — , List<String>, List<Integer>etc.

Kommer du förresten ihåg vad varargs är?

Om du glömde, är detta ett argument med variabel längd. De är användbara i situationer där vi inte vet hur många argument som kan skickas till vår metod. Till exempel om vi har en kalkylatorklass som har en summetod. Metoden sum()kan ta emot 2 nummer, eller 3, eller 5, eller så många du vill. Det skulle vara väldigt konstigt att överbelasta sum()metoden för alla möjliga antal argument. Istället kan vi göra så här:

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));
   }
}
Konsolutgång:

15
11
Detta visar oss att det finns några viktiga funktioner när man använder varargs i kombination med generika. Låt oss titta på följande kod:

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 indata ett List<E>och valfritt antal Eobjekt, och sedan lägger den till alla dessa objekt i listan. I main()metoden kallar vi vår addAll()metod två gånger. I det första fallet lägger vi till två vanliga strängar till List. Allt är i sin ordning här. I det andra fallet lägger vi till två Pair<String, String>objekt till List. Men här får vi oväntat en varning:

Unchecked generics array creation for varargs parameter
Vad betyder det? Varför får vi en varning och varför nämns det något om en array? Vår kod har trots allt inte ett array! Låt oss börja med det andra fallet. Varningen nämner en array eftersom kompilatorn konverterar argumentet med variabel längd (varargs) till en array. Med andra ord, signaturen för vår addAll()metod är:

public static <E> void addAll(List<E> list, E... array)
Det ser faktiskt ut så här:

public static <E> void addAll(List<E> list, E[] array)
Det vill säga i main()metoden konverterar kompilatorn vår kod till detta:

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 är bara bra. Men en Pair<String, String>array är det inte. Problemet är att det Pair<String, String>är en icke återbetalningsbar typ. Under kompileringen raderas all information om typargument (<String, String>). Det är inte tillåtet att skapa arrayer av en typ som inte kan återföras i Java . Du kan se detta om du försöker skapa en array för par<String, String> manuellt

public static void main(String[] args) {

   // Compilation error Generic array creation
  Pair<String, String>[] array = new Pair<String, String>[10];
}
Anledningen är uppenbar: typsäkerhet. Som du kommer ihåg, när du skapar en array måste du definitivt ange vilka objekt (eller primitiver) som arrayen kommer att lagra.

int array[] = new int[10];
I en av våra tidigare lektioner undersökte vi typradering i detalj. I det här fallet gör typradering att vi förlorar informationen som Pairobjekten lagrar <String, String>par. Att skapa arrayen skulle vara osäkert. När du använder metoder som involverar varargs och generika, var noga med att komma ihåg om typradering och hur det fungerar. Om du är helt säker på koden du har skrivit och vet att den inte kommer att orsaka några problem, kan du stänga av varargs-relaterade varningar med hjälp av anteckningarna @SafeVarargs.

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

   for (E element : array) {
       list.add(element);
   }
}
Om du lägger till den här anteckningen i din metod kommer varningen vi stötte på tidigare inte att visas. Ett annat problem som kan uppstå när man använder varargs med generika är högföroreningar. Använda varargs när du arbetar med generika - 3Högförorening kan inträffa i följande 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));
   }
}
Konsolutgång:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Enkelt uttryckt är högföroreningar när objekt av typ Aska finnas i högen, men objekt av typ Bhamnar där på grund av fel relaterade till typsäkerhet. I vårt exempel är det precis vad som händer. Först skapade vi råvariabeln numbersoch tilldelade en generisk samling ( ArrayList<Number>) till den. Sedan lade vi till numret 1i samlingen.

List<String> strings = numbers;
På den här raden försökte kompilatorn varna oss för möjliga fel genom att utfärda varningen " Okontrollerad tilldelning... ", men vi ignorerade den. Vi slutar med en generisk variabel av typ List<String>som pekar på en generisk samling av typ ArrayList<Number>. Det är klart att denna situation kan leda till problem! Och så gör det. Med vår nya variabel lägger vi till en sträng i samlingen. Vi har nu högföroreningar — vi lade till ett nummer och sedan en sträng till den parametriserade samlingen. Kompilatorn varnade oss, men vi ignorerade dess varning. Som ett resultat får vi en ClassCastExceptionendast medan programmet körs. Så vad har detta med varargs att göra? Att använda varargs med generika kan lätt leda till högföroreningar. Här är ett enkelt exempel:

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);
   }
}
Vad händer här? På grund av typradering, vårt argument med variabel längd

List<String>...stringsLists
blir en array av listor, dvs List[]av objekt av en okänd typ (glöm inte att varargs förvandlas till en vanlig array under kompileringen). På grund av detta kan vi enkelt tilldela den till Object[] arrayvariabeln i metodens första rad — typen av objekt i våra listor har raderats! Och nu har vi en Object[]variabel, till vilken vi kan lägga till vad som helst, eftersom alla objekt i Java ärver Object! Till en början har vi bara en rad listor med strängar. Men tack vare typradering och vår användning av varargs kan vi enkelt lägga till en lista med nummer, vilket vi gör. Som ett resultat förorenar vi högen genom att blanda föremål av olika typer. Resultatet blir ännu ett ClassCastExceptionnär vi försöker läsa en sträng från arrayen. Konsolutgång:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Sådana oväntade konsekvenser kan orsakas av att använda varargs, en till synes enkel mekanism :) Och med det tar dagens lektion ett slut. Glöm inte att lösa ett par uppgifter, och om du har tid och ork, studera lite extra läsning. " Effektiv Java " läser inte sig själv! :) Tills nästa gång!
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION