CodeGym /Java blog /Véletlen /Varargok használata generikus gyógyszerekkel végzett munk...
John Squirrels
Szint
San Francisco

Varargok használata generikus gyógyszerekkel végzett munka során

Megjelent a csoportban
Szia! A mai leckében folytatjuk a generikumok tanulmányozását. Megtörténhet, hogy ez egy nagy téma, de nem lehet megkerülni – ez a nyelv rendkívül fontos része :) Amikor áttanulmányozza az Oracle generikusokról szóló dokumentációját vagy online oktatóanyagokat olvas, találkozni fog a nem újraírható típusok kifejezésekkel , reifikálható típusok . Az újraértékelhető típus olyan típus, amelyre vonatkozó információk futás közben teljes mértékben rendelkezésre állnak. A Java-ban az ilyen típusok közé tartoznak a primitívek, a nyers típusok és a nem általános típusok. Ezzel szemben a nem újraírható típusok olyan típusok, amelyek információi törlődnek, és futás közben elérhetetlenné válnak. Ezek általánosságok – List<String>, List<Integer>, stb.

Egyébként emlékszel, mi az a varargs?

Ha elfelejtette, ez egy változó hosszúságú argumentum. Olyan helyzetekben hasznosak, amikor nem tudjuk, hány érvet adhatunk át a módszerünknek. Például, ha van egy számológép-osztályunk, amelynek van summetódusa. A sum()módszer fogadhat 2 számot, vagy 3-at, vagy 5-öt, vagy annyit, amennyit csak akar. Nagyon furcsa lenne túlterhelni a sum()metódust minden lehetséges számú argumentum esetén. Ehelyett ezt tehetjük:

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

15
11
Ez azt mutatja, hogy van néhány fontos funkció, amikor a varargokat generikusokkal kombináljuk. Nézzük a következő kódot:

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")
       );
   }
}
A addAll()metódus bemenetként a List<E>és tetszőleges számú Eobjektumot vesz fel, majd hozzáadja ezeket az objektumokat a listához. A metódusban kétszer main()hívjuk meg a módszerünket. addAll()Az első esetben két közönséges karakterláncot adunk a List. Itt minden rendben van. A második esetben két Pair<String, String>objektumot adunk a List. De itt váratlanul kapunk egy figyelmeztetést:

Unchecked generics array creation for varargs parameter
Az mit jelent? Miért kapunk figyelmeztetést, és miért említik a array? Végül is a kódunkban nincs array! Kezdjük a második esettel. A figyelmeztetés egy tömböt említ, mert a fordító a változó hosszúságú argumentumot (varargs) tömbbé alakítja. Más szavakkal, módszerünk aláírása a addAll()következő:

public static <E> void addAll(List<E> list, E... array)
Valójában így néz ki:

public static <E> void addAll(List<E> list, E[] array)
Vagyis a metódusban main()a fordító a kódunkat erre konvertálja:

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") 
        } 
   ); 
}
Egy Stringtömb jó. De egy Pair<String, String>tömb nem az. A probléma az, hogy Pair<String, String>egy nem visszafizethető típus. A fordítás során a típusargumentumok (<String, String>) összes információja törlődik. Nem újraírható típusú tömbök létrehozása nem engedélyezett a Java-ban . Ezt láthatja, ha manuálisan próbál létrehozni egy Pair<String, String> tömböt

public static void main(String[] args) {

   // Compilation error Generic array creation
  Pair<String, String>[] array = new Pair<String, String>[10];
}
Az ok nyilvánvaló: típusbiztonság. Amint emlékszel, egy tömb létrehozásakor feltétlenül meg kell adni, hogy a tömb mely objektumokat (vagy primitíveket) tárolja.

int array[] = new int[10];
Egyik korábbi leckénkben részletesen megvizsgáltuk a típustörlést. Ebben az esetben a típustörlés hatására elveszítjük azokat az információkat, amelyeket az Pairobjektumok párokat tárolnak <String, String>. A tömb létrehozása nem lenne biztonságos. Ha varargokat és generikus módszereket használ , ne felejtse el a típustörlést és annak működését. Ha teljesen biztos az általad írt kódban, és tudja, hogy az nem okoz problémát, akkor a megjegyzések segítségével kikapcsolhatja a varargs-szal kapcsolatos figyelmeztetéseket . @SafeVarargs

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

   for (E element : array) {
       list.add(element);
   }
}
Ha hozzáadja ezt a megjegyzést a metódusához, akkor a korábban tapasztalt figyelmeztetés nem jelenik meg. Egy másik probléma, amely a varargok generikus gyógyszerekkel történő használatakor előfordulhat, a halomszennyezés. Varargok használata generikus gyógyszerekkel végzett munka során - 3A halomszennyezés a következő esetekben fordulhat elő:

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

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Egyszerűen fogalmazva, halomszennyezésről beszélünk, amikor egy típusú objektumnak Aa kupacban kell lennie, de a típusú objektumok Ba típusbiztonsággal kapcsolatos hibák miatt kerülnek oda. Példánkban pontosan ez történik. Először létrehoztuk a nyers numbersváltozót, és hozzárendeltünk egy általános gyűjteményt ( ArrayList<Number>). Aztán hozzáadtuk a számot 1a gyűjteményhez.

List<String> strings = numbers;
Ezen a vonalon a fordító megpróbált figyelmeztetni minket a lehetséges hibákra az " Unchecked assignment... " figyelmeztetéssel, de figyelmen kívül hagytuk. A végén egy általános típusú változót kapunk, List<String>amely egy általános típusgyűjteményre mutat ArrayList<Number>. Nyilvánvaló, hogy ez a helyzet bajhoz vezethet! És így is van. Az új változónk segítségével egy karakterláncot adunk a gyűjteményhez. Most halom szennyezés van – hozzáadtunk egy számot, majd egy karakterláncot a parametrizált gyűjteményhez. A fordító figyelmeztetett minket, de mi figyelmen kívül hagytuk a figyelmeztetését. ClassCastExceptionEnnek eredményeként a csak a program futása közben kapunk . Szóval mi köze ennek a varargokhoz? A varargok generikus gyógyszerekkel való használata könnyen halomszennyezéshez vezethet. Íme egy egyszerű példa:

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);
   }
}
Mi folyik itt? A típustörlés miatt a változó hosszúságú argumentumunk

List<String>...stringsLists
listák tömbjévé válik, azaz List[]ismeretlen típusú objektumok tömbjévé (ne felejtsük el, hogy a varargs a fordítás során szabályos tömbbé válik). Emiatt könnyen hozzárendelhetjük a Object[] arraymetódus első sorában lévő változóhoz – a listánkban szereplő objektumok típusa törölve lett! És most van egy Object[]változónk, amihez bármit hozzáadhatunk, hiszen a Java-ban minden objektum örökli Object! Eleinte csak a karakterláncok listáinak tömbje áll rendelkezésünkre. De a típustörlésnek és a varargok használatának köszönhetően könnyen hozzáadhatunk egy számlistát, amit meg is teszünk. Ennek eredményeként különböző típusú tárgyak keverésével szennyezzük a kupacot. Az eredmény még más lesz, ClassCastExceptionamikor megpróbálunk kiolvasni egy karakterláncot a tömbből. Konzol kimenet:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Ilyen váratlan következményeket okozhat a varargok használata, egy egyszerűnek tűnő mechanizmus :) És ezzel a mai lecke is véget ér. Ne felejts el megoldani néhány feladatot, és ha van időd és energiád, tanulj meg néhány további olvasmányt. Az " effektív Java " nem olvassa be magát! :) A következő alkalomig!
Hozzászólások
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION