JavaRush /Java Blog /Random-TL /Mga Wildcard sa Java Generics

Mga Wildcard sa Java Generics

Nai-publish sa grupo
Kamusta! Patuloy naming pinag-aaralan ang paksa ng generics. Mayroon ka nang sapat na kaalaman tungkol sa mga ito mula sa mga nakaraang lektura (tungkol sa paggamit ng mga varargs kapag nagtatrabaho sa mga generic at tungkol sa pagbubura ng uri ), ngunit wala pa kaming sakop ng isang mahalagang paksa: mga wildcard . Ito ay isang napakahalagang katangian ng generics. Kaya't naglaan kami ng isang hiwalay na panayam para dito! Gayunpaman, walang kumplikado tungkol sa mga wildcard, makikita mo iyon ngayon :) Mga wildcard sa generics - 1Tingnan natin ang isang halimbawa:
public class Main {

   public static void main(String[] args) {

       String str = new String("Test!");
       // ниHowих проблем
       Object obj = str;

       List<String> strings = new ArrayList<String>();
       // ошибка компиляции!
       List<Object> objects = strings;
   }
}
Anong nangyayari dito? Nakikita natin ang dalawang magkatulad na sitwasyon. Sa una sa mga ito, sinusubukan naming mag-cast ng isang bagay Stringupang i-type Object. Walang mga problema dito, gumagana ang lahat ayon sa nararapat. Ngunit sa pangalawang sitwasyon, ang tagatala ay nagtatapon ng isang error. Bagama't mukhang pareho ang ginagawa namin. Ngayon lang ay gumagamit kami ng isang koleksyon ng ilang mga bagay. Ngunit bakit nangyayari ang pagkakamali? Ano ang pagkakaiba, mahalagang, kung itinapon natin ang isang bagay Stringsa isang uri Objecto 20 bagay? Mayroong mahalagang pagkakaiba sa pagitan ng isang bagay at isang koleksyon ng mga bagay . Kung ang isang klase ay isang kahalili ng isang klase , kung gayon ito ay hindi isang kahalili . Ito ay para sa kadahilanang ito na hindi namin maaaring dalhin sa amin sa . ay tagapagmana , ngunit hindi tagapagmana . Sa madaling salita, hindi ito mukhang masyadong lohikal. Bakit ginabayan ng prinsipyong ito ang mga lumikha ng wika? Isipin natin na hindi tayo bibigyan ng compiler ng error dito: BАCollection<B>Collection<A>List<String>List<Object>StringObjectList<String>List<Object>
List<String> strings = new ArrayList<String>();
List<Object> objects = strings;
Sa kasong ito, maaari naming, halimbawa, gawin ang sumusunod:
objects.add(new Object());
String s = strings.get(0);
Dahil hindi kami binigyan ng compiler ng mga error at pinahintulutan kaming lumikha ng isang sanggunian List<Object> objectsa isang koleksyon ng mga string strings, hindi kami maaaring magdagdag stringsng isang string, ngunit simpleng anumang bagay Object! Kaya, nawalan kami ng garantiya na tanging ang mga bagay na tinukoy sa generic ang nasa aming koleksyonString . Iyon ay, nawala sa amin ang pangunahing bentahe ng generics - uri ng kaligtasan. At dahil pinahintulutan kami ng compiler na gawin ang lahat ng ito, nangangahulugan ito na makakakuha lamang kami ng isang error sa panahon ng pagpapatupad ng programa, na palaging mas masahol kaysa sa isang error sa compilation. Upang maiwasan ang mga ganitong sitwasyon, binibigyan kami ng compiler ng error:
// ошибка компиляции
List<Object> objects = strings;
...at ipinaalala sa kanya na List<String>hindi siya tagapagmana List<Object>. Ito ay isang mahigpit na panuntunan para sa pagpapatakbo ng mga generic, at dapat itong tandaan kapag ginagamit ang mga ito. Mag-move on na tayo. Sabihin nating mayroon tayong maliit na hierarchy ng klase:
public class Animal {

   public void feed() {

       System.out.println("Animal.feed()");
   }
}

public class Pet extends Animal {

   public void call() {

       System.out.println("Pet.call()");
   }
}

public class Cat extends Pet {

   public void meow() {

       System.out.println("Cat.meow()");
   }
}
Sa pinuno ng hierarchy ay simpleng Mga Hayop: Ang mga alagang hayop ay minana mula sa kanila. Ang mga alagang hayop ay nahahati sa 2 uri - Aso at Pusa. Ngayon isipin na kailangan nating lumikha ng isang simpleng paraan iterateAnimals(). Ang pamamaraan ay dapat tumanggap ng isang koleksyon ng anumang mga hayop ( Animal, Pet, Cat, Dog), umulit sa lahat ng mga elemento, at maglabas ng isang bagay sa console sa bawat oras. Subukan nating isulat ang pamamaraang ito:
public static void iterateAnimals(Collection<Animal> animals) {

   for(Animal animal: animals) {

       System.out.println("Еще один шаг в цикле пройден!");
   }
}
Mukhang nalutas na ang problema! Gayunpaman, tulad ng nalaman namin kamakailan, List<Cat>o List<Dog>hindi List<Pet>mga tagapagmana List<Animal>! Samakatuwid, kapag sinubukan naming tumawag sa isang paraan iterateAnimals()na may listahan ng mga pusa, makakatanggap kami ng error sa compiler:
import java.util.*;

public class Main3 {


   public static void iterateAnimals(Collection<Animal> animals) {

       for(Animal animal: animals) {

           System.out.println("Еще один шаг в цикле пройден!");
       }
   }

   public static void main(String[] args) {


       List<Cat> cats = new ArrayList<>();
       cats.add(new Cat());
       cats.add(new Cat());
       cats.add(new Cat());
       cats.add(new Cat());

       //ошибка компилятора!
       iterateAnimals(cats);
   }
}
Hindi maganda ang sitwasyon para sa atin! Lumalabas na kailangan nating magsulat ng hiwalay na mga pamamaraan para sa pag-enumerate ng lahat ng uri ng hayop? Sa totoo lang, hindi, hindi mo kailangang :) At ang mga wildcard ay makakatulong sa amin dito ! Malulutas namin ang problema sa loob ng isang simpleng pamamaraan, gamit ang sumusunod na konstruksyon:
public static void iterateAnimals(Collection<? extends Animal> animals) {

   for(Animal animal: animals) {

       System.out.println("Еще один шаг в цикле пройден!");
   }
}
Ito ang wildcard. Mas tiyak, ito ang una sa ilang uri ng wildcard - “ extends ” (isa pang pangalan ay Upper Bounded Wildcards ). Ano ang sinasabi sa atin ng disenyong ito? Nangangahulugan ito na ang pamamaraan ay tumatagal bilang input ng isang koleksyon ng mga bagay sa klase Animalo mga bagay ng anumang descendant na klase Animal (? extends Animal). AnimalSa madaling salita, maaaring tanggapin ng pamamaraan ang koleksyon , Pet, Dogo bilang input Cat- wala itong pinagkaiba. Siguraduhin nating gumagana ito:
public static void main(String[] args) {

   List<Animal> animals = new ArrayList<>();
   animals.add(new Animal());
   animals.add(new Animal());

   List<Pet> pets = new ArrayList<>();
   pets.add(new Pet());
   pets.add(new Pet());

   List<Cat> cats = new ArrayList<>();
   cats.add(new Cat());
   cats.add(new Cat());

   List<Dog> dogs = new ArrayList<>();
   dogs.add(new Dog());
   dogs.add(new Dog());

   iterateAnimals(animals);
   iterateAnimals(pets);
   iterateAnimals(cats);
   iterateAnimals(dogs);
}
Output ng console:

Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Nakagawa kami ng kabuuang 4 na koleksyon at 8 bagay, at may eksaktong 8 entry sa console. Lahat ay gumagana nang mahusay! :) Pinahintulutan kami ng wildcard na madaling magkasya sa kinakailangang lohika na may pagbubuklod sa mga partikular na uri sa isang paraan. Inalis namin ang pangangailangan na magsulat ng isang hiwalay na paraan para sa bawat uri ng hayop. Isipin kung gaano karaming mga pamamaraan ang mayroon tayo kung ang aming aplikasyon ay ginamit sa isang zoo o isang beterinaryo na klinika :) Ngayon tingnan natin ang ibang sitwasyon. Ang aming inheritance hierarchy ay mananatiling pareho: ang pinakamataas na antas na klase ay Animal, nasa ibaba lamang ang klase na Mga Alagang Hayop Pet, at sa susunod na antas ay Catat Dog. Ngayon ay kailangan mong muling isulat ang pamamaraan iretateAnimals()upang maaari itong gumana sa anumang uri ng hayop maliban sa mga aso . Collection<Animal>Iyon ay, dapat itong tanggapin , Collection<Pet>o bilang input Collection<Cat>, ngunit hindi dapat gumana sa Collection<Dog>. Paano natin ito makakamit? Tila na ang pag-asam ng pagsusulat ng isang hiwalay na paraan para sa bawat uri ay lumalapit muli sa atin :/ Paano pa natin maipapaliwanag ang ating lohika sa compiler? At ito ay maaaring gawin nang napakasimple! Narito ang mga wildcard ay muling tutulong sa atin. Ngunit sa pagkakataong ito ay gagamit tayo ng ibang uri - “ super ” (isa pang pangalan ay Lower Bounded Wildcards ).
public static void iterateAnimals(Collection<? super Cat> animals) {

   for(int i = 0; i < animals.size(); i++) {

       System.out.println("Еще один шаг в цикле пройден!");
   }
}
Ang prinsipyo ay katulad dito. Ang konstruksiyon <? super Cat>ay nagsasabi sa tagatala na ang pamamaraan iterateAnimals()ay maaaring kunin bilang input ng isang koleksyon ng mga bagay ng klase Cato anumang iba pang ancestor class Cat. CatSa aming kaso, ang klase mismo , ang ninuno nito - Pets, at ang ninuno ng ninuno - ay angkop sa paglalarawang ito Animal. Ang klase Dogay hindi umaangkop sa pagpilit na ito, at samakatuwid ang pagtatangka na gumamit ng isang pamamaraan na may isang listahan List<Dog>ay magreresulta sa isang error sa compilation:
public static void main(String[] args) {

   List<Animal> animals = new ArrayList<>();
   animals.add(new Animal());
   animals.add(new Animal());

   List<Pet> pets = new ArrayList<>();
   pets.add(new Pet());
   pets.add(new Pet());

   List<Cat> cats = new ArrayList<>();
   cats.add(new Cat());
   cats.add(new Cat());

   List<Dog> dogs = new ArrayList<>();
   dogs.add(new Dog());
   dogs.add(new Dog());

   iterateAnimals(animals);
   iterateAnimals(pets);
   iterateAnimals(cats);

   //ошибка компиляции!
   iterateAnimals(dogs);
}
Ang aming problema ay nalutas, at muli ang mga wildcard ay naging lubhang kapaki-pakinabang :) Ito ay nagtatapos sa panayam. Ngayon nakita mo kung gaano kahalaga ang paksa ng generics kapag nag-aaral ng Java - gumugol kami ng 4 na lektura dito! Ngunit ngayon ay mayroon kang mahusay na pagkaunawa sa paksa at magagawa mong patunayan ang iyong sarili sa panayam :) At ngayon na ang oras upang bumalik sa mga gawain! Good luck sa iyong pag-aaral! :)
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION