CodeGym /Java Blog /Random /Paggamit ng varargs kapag nagtatrabaho sa generics
John Squirrels
Antas
San Francisco

Paggamit ng varargs kapag nagtatrabaho sa generics

Nai-publish sa grupo
Hi! Sa aralin ngayon, magpapatuloy tayo sa pag-aaral ng generics. Habang nangyayari ito, ito ay isang malaking paksa, ngunit hindi ito maiiwasan — ito ay isang napakahalagang bahagi ng wika :) Kapag pinag-aralan mo ang dokumentasyon ng Oracle sa mga generic o nagbasa ng mga online na tutorial, makikita mo ang mga terminong hindi nare-reifiable na mga uri at reifiable na mga uri . Ang reifiable na uri ay isang uri kung saan ang impormasyon ay ganap na magagamit sa runtime. Sa Java, ang mga ganitong uri ay kinabibilangan ng mga primitive, raw na uri, at hindi pangkaraniwang uri. Sa kabaligtaran, ang mga non-reifiable na uri ay mga uri na ang impormasyon ay nabubura at nagiging hindi naa-access sa runtime. Habang nangyayari, ito ay mga generics — List<String>, List<Integer>, atbp.

By the way, naalala mo ba kung ano ang varargs?

Kung sakaling nakalimutan mo, ito ay isang variable-length na argumento. Ang mga ito ay kapaki-pakinabang sa mga sitwasyon kung saan hindi natin alam kung gaano karaming mga argumento ang maaaring maipasa sa ating pamamaraan. Halimbawa, kung mayroon tayong klase ng calculator na mayroong sumpamamaraan. Ang sum()pamamaraan ay maaaring makatanggap ng 2 numero, o 3, o 5, o hangga't gusto mo. Magiging kakaiba ang labis na karga ng sum()pamamaraan para sa bawat posibleng bilang ng mga argumento. Sa halip, magagawa natin ito:

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));
   }
}
Output ng console:

15
11
Ipinapakita nito sa amin na mayroong ilang mahahalagang tampok kapag gumagamit ng mga varargs kasama ng mga generic. Tingnan natin ang sumusunod na code:

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")
       );
   }
}
Ang addAll()pamamaraan ay tumatagal bilang input a List<E>at anumang bilang ng Emga bagay, at pagkatapos ay idinaragdag nito ang lahat ng mga bagay na ito sa listahan. Sa main()pamamaraan, tinawag namin ang aming addAll()pamamaraan nang dalawang beses. Sa unang kaso, nagdaragdag kami ng dalawang ordinaryong string sa List. Nakaayos na ang lahat dito. Sa pangalawang kaso, nagdaragdag kami ng dalawang Pair<String, String>bagay sa List. Ngunit dito kami ay hindi inaasahang nakatanggap ng babala:

Unchecked generics array creation for varargs parameter
Anong ibig sabihin niyan? Bakit tayo nakakakuha ng babala at bakit mayroong anumang pagbanggit ng isang array? Pagkatapos ng lahat, ang aming code ay walang array! Magsimula tayo sa pangalawang kaso. Binabanggit ng babala ang isang array dahil kino-convert ng compiler ang variable-length na argumento (varargs) sa isang array. Sa madaling salita, ang lagda ng aming addAll()pamamaraan ay:

public static <E> void addAll(List<E> list, E... array)
Ito ay talagang ganito:

public static <E> void addAll(List<E> list, E[] array)
Iyon ay, sa main()pamamaraan, kino-convert ng compiler ang aming code sa ganito:

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") 
        } 
   ); 
}
Ang isang Stringarray ay maayos lang. Ngunit ang isang Pair<String, String>array ay hindi. Ang problema ay iyon Pair<String, String>ay isang non-reifiable na uri. Sa panahon ng compilation, ang lahat ng impormasyon tungkol sa mga uri ng argumento (<String, String>) ay mabubura. Hindi pinapayagan sa Java ang paggawa ng mga array ng hindi reifiable na uri . Makikita mo ito kung susubukan mong manu-manong lumikha ng array ng Pair<String, String>

public static void main(String[] args) {

   // Compilation error Generic array creation
  Pair<String, String>[] array = new Pair<String, String>[10];
}
Ang dahilan ay malinaw: uri ng kaligtasan. Tulad ng maaalala mo, kapag lumilikha ng isang array, tiyak na kailangan mong tukuyin kung aling mga bagay (o primitives) ang iimbak ng array.

int array[] = new int[10];
Sa isa sa aming mga nakaraang aralin, sinuri namin ang uri ng pagbura nang detalyado. Sa kasong ito, ang pagbubura ng uri ay nagdudulot sa amin na mawala ang impormasyong Pairiniimbak ng mga bagay <String, String>na pares. Ang paggawa ng array ay hindi ligtas. Kapag gumagamit ng mga pamamaraan na kinasasangkutan ng mga varargs at generics, siguraduhing tandaan ang tungkol sa uri ng pagbura at kung paano ito gumagana. Kung talagang sigurado ka tungkol sa code na iyong isinulat at alam mong hindi ito magdudulot ng anumang problema, maaari mong i-off ang mga babala na nauugnay sa varargs gamit ang mga @SafeVarargsanotasyon.

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

   for (E element : array) {
       list.add(element);
   }
}
Kung idaragdag mo ang anotasyong ito sa iyong pamamaraan, hindi lalabas ang babalang naranasan namin kanina. Ang isa pang problema na maaaring mangyari kapag gumagamit ng varargs na may mga generic ay ang heap pollution. Paggamit ng varargs kapag nagtatrabaho sa generics - 3Maaaring mangyari ang heap pollution sa sumusunod na sitwasyon:

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));
   }
}
Output ng console:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Sa madaling salita, ang heap pollution ay kapag ang mga bagay na may uri Aay dapat nasa heap, ngunit ang mga bagay na may uri Bay napupunta doon dahil sa mga error na nauugnay sa kaligtasan ng uri. Sa aming halimbawa, ito mismo ang nangyayari. Una, ginawa namin ang raw numbersvariable at nagtalaga ng generic na koleksyon ( ArrayList<Number>) dito. Pagkatapos ay idinagdag namin ang numero 1sa koleksyon.

List<String> strings = numbers;
Sa linyang ito, sinubukan kaming bigyan ng babala ng compiler tungkol sa mga posibleng error sa pamamagitan ng pagbibigay ng babala na " Hindi na-check ang pagtatalaga... ", ngunit hindi namin ito pinansin. Napupunta kami sa isang generic na variable ng uri List<String>na tumuturo sa isang generic na koleksyon ng uri ArrayList<Number>. Maliwanag, ang sitwasyong ito ay maaaring humantong sa problema! At ganoon din ang ginagawa nito. Gamit ang aming bagong variable, nagdaragdag kami ng string sa koleksyon. Mayroon na kaming heap pollution — nagdagdag kami ng numero at pagkatapos ay isang string sa parametrized na koleksyon. Binalaan kami ng compiler, ngunit hindi namin pinansin ang babala nito. Bilang resulta, nakakakuha ClassCastExceptionlamang kami ng isang habang tumatakbo ang programa. Kaya ano ang kinalaman nito sa varargs? Ang paggamit ng mga varargs na may mga generic ay madaling humantong sa tambak na polusyon. Narito ang isang simpleng halimbawa:

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);
   }
}
Anong nangyayari dito? Dahil sa type erasure, ang aming variable-length na argumento

List<String>...stringsLists
ay nagiging isang hanay ng mga listahan, ibig sabihin List[], ng mga bagay na hindi kilalang uri (huwag kalimutan na ang mga varargs ay nagiging isang regular na hanay sa panahon ng compilation). Dahil dito, madali naming maitalaga ito sa Object[] arrayvariable sa unang linya ng pamamaraan — ang uri ng mga bagay sa aming mga listahan ay nabura! At ngayon mayroon kaming isang Object[]variable, kung saan maaari kaming magdagdag ng kahit ano, dahil ang lahat ng mga bagay sa Java ay nagmamana Object! Sa una, mayroon lang kaming hanay ng mga listahan ng mga string. Ngunit salamat sa uri ng erasure at sa aming paggamit ng mga varargs, madali kaming makakapagdagdag ng listahan ng mga numero, na ginagawa namin. Bilang resulta, dinudumhan natin ang bunton sa pamamagitan ng paghahalo ng mga bagay na may iba't ibang uri. Ang resulta ay isa pa ClassCastExceptionkapag sinubukan naming basahin ang isang string mula sa array. Output ng console:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Ang ganitong mga hindi inaasahang kahihinatnan ay maaaring sanhi ng paggamit ng varargs, isang tila simpleng mekanismo :) At kasama nito, ang aralin ngayon ay nagtatapos. Huwag kalimutang lutasin ang ilang mga gawain, at kung mayroon kang oras at lakas, pag-aralan ang ilang karagdagang pagbabasa. Ang " Effective Java " ay hindi magbabasa mismo! :) Hanggang sa muli!
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION