CodeGym/Java Blog/Random/Mga wildcard sa generics
John Squirrels
Antas
San Francisco

Mga wildcard sa generics

Nai-publish sa grupo
Hi! Ipagpatuloy natin ang ating pag-aaral ng generics. Nakakuha ka na ng malaking kaalaman tungkol sa mga ito mula sa mga nakaraang aralin (tungkol sa paggamit ng mga varargs kapag nagtatrabaho sa mga generic at tungkol sa pagbubura ng uri ), ngunit may mahalagang paksa na hindi pa namin isinasaalang-alang — mga wildcard . Ito ay napakahalagang katangian ng generics. Kaya't naglaan kami ng isang hiwalay na aralin dito! Sabi nga, walang partikular na kumplikado tungkol sa mga wildcard. Makikita mo iyon kaagad :) Mga wildcard sa generics - 1Tingnan natin ang isang halimbawa:
public class Main {

   public static void main(String[] args) {

       String str = new String("Test!");
       // No problem
       Object obj = str;

       List<String> strings = new ArrayList<String>();
       // Compilation error!
       List<Object> objects = strings;
   }
}
Anong nangyayari dito? Nakikita natin ang dalawang magkatulad na sitwasyon. Sa kaso, naglagay kami ng isang Stringbagay sa isang Objectbagay. Walang mga problema dito — gumagana ang lahat gaya ng inaasahan. Ngunit sa pangalawang sitwasyon, ang compiler ay bumubuo ng isang error. Pero pareho naman tayo ng ginagawa di ba? Sa pagkakataong ito, gumagamit lang kami ng koleksyon ng ilang bagay. Ngunit bakit nangyayari ang pagkakamali? Ano ang pinagkaiba? Naghahagis ba tayo ng isang Stringbagay sa isang Objecto 20 bagay? Mayroong mahalagang pagkakaiba sa pagitan ng isang bagay at isang koleksyon ng mga bagay . Kung ang Bklase ay anak ng Aklase, kung gayon Collection<B>ay hindi anak ng Collection<A>. Ito ang dahilan kung bakit hindi namin nai-cast ang aming List<String>sa aList<Object>. Stringay anak ni Object, ngunit List<String>hindi anak ni List<Object>. Maaaring hindi ito mukhang sobrang intuitive. Bakit ginawa ito ng mga tagalikha ng wika sa ganitong paraan? Isipin natin na ang compiler ay hindi nagbibigay sa amin ng isang error:
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 ang compiler ay hindi nagbigay sa amin ng anumang error at pinahintulutan kaming lumikha ng isang List<Object>sanggunian na tumuturo sa strings, maaari kaming magdagdag ng anumang lumang Objectbagay sa stringskoleksyon! Kaya, nawalan kami ng garantiya na ang aming koleksyon ay naglalaman lamang ng Stringmga bagay na tinukoy ng uri ng argumento sa generic na uri ng invocation . Sa madaling salita, nawala sa amin ang pangunahing bentahe ng generics — uri ng kaligtasan. At dahil hindi kami pinigilan ng compiler na gawin ito, makakakuha lang kami ng error sa oras ng pagtakbo, na palaging mas malala kaysa sa error sa compilation. Upang maiwasan ang mga sitwasyong tulad nito, binibigyan kami ng compiler ng error:
// Compilation error
List<Object> objects = strings;
...at nagpapaalala sa atin na List<String>hindi inapo ni List<Object>. Ito ay isang mahigpit na panuntunan para sa mga generic, at dapat itong tandaan kapag nagtatrabaho sa kanila. Mag-move on na tayo. Ipagpalagay na mayroon kaming isang 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()");
   }
}
Ang hierarchy ay nangunguna sa isang simpleng klase ng Hayop, na minana ng Pet. May 2 subclass ang alagang hayop: Aso at Pusa. Ngayon ipagpalagay na kailangan nating lumikha ng isang simpleng iterateAnimals()pamamaraan. Ang pamamaraan ay dapat kumuha ng koleksyon ng anumang mga hayop ( Animal, Pet, Cat, Dog), umulit sa lahat ng elemento, at magpakita ng mensahe sa console sa bawat pag-ulit. Subukan nating magsulat ng ganitong paraan:
public static void iterateAnimals(Collection<Animal> animals) {

   for(Animal animal: animals) {

       System.out.println("Another iteration in the loop!");
   }
}
Mukhang nalutas na ang problema! Gayunpaman, tulad ng nalaman natin kamakailan, List<Cat>, List<Dog>at List<Pet>hindi mga inapo ng List<Animal>! Nangangahulugan ito na kapag sinubukan naming tawagan ang iterateAnimals()pamamaraan na may listahan ng mga pusa, nakakakuha kami ng error sa compilation:
import java.util.*;

public class Main3 {


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

       for(Animal animal: animals) {

           System.out.println("Another iteration in the loop!");
       }
   }

   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());

       // Compilation error!
       iterateAnimals(cats);
   }
}
Mukhang hindi maganda ang sitwasyon para sa amin! Kailangan ba nating sumulat ng magkakahiwalay na pamamaraan para sa pag-enumerate ng bawat uri ng hayop? Sa totoo lang, hindi, hindi namin ginagawa :) At habang nangyayari ito, tinutulungan kami ng mga wildcard dito! Malutas natin ang problema sa isang simpleng pamamaraan gamit ang sumusunod na konstruksyon:
public static void iterateAnimals(Collection<? extends Animal> animals) {

   for(Animal animal: animals) {

       System.out.println("Another iteration in the loop!");
   }
}
Ito ay isang wildcard. Mas tiyak, ito ang una sa ilang uri ng mga wildcard. Ito ay kilala bilang isang upper-bounded wildcard at ipinapahayag ng ? umaabot . Ano ang sinasabi sa atin ng construct na ito? Nangangahulugan ito na ang pamamaraan ay tumatanggap ng isang koleksyon ng Animalmga bagay o isang koleksyon ng mga bagay ng anumang klase na nagmula sa Animal(? extends Animal). Sa madaling salita, ang pamamaraan ay maaaring tumanggap ng isang koleksyon ng Animal, Pet, Dog, o Catmga bagay — wala itong pinagkaiba. Kumbinsihin natin ang ating sarili na ito ay gumagana:
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:
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Gumawa 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 ang kinakailangang lohika na nakatali 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 kailangan namin kung ang aming aplikasyon ay ginamit ng isang zoo o isang opisina ng beterinaryo :) Ngunit ngayon tingnan natin ang ibang sitwasyon. Ang aming inheritance hierarchy ay nananatiling hindi nagbabago: ang pinakamataas na antas na klase ay Animal, na ang Petklase ay nasa ibaba lamang, at ang Catat Dogmga klase sa susunod na antas. Ngayon ay kailangan mong muling isulat ang iterateAnimals()pamamaraan upang gumana sa anumang uri ng hayop, maliban sa mga aso . Ibig sabihin, dapat tanggapin Collection<Animal>,Collection<Pet>o Collection<Car>, ngunit hindi ito dapat gumana sa Collection<Dog>. Paano natin ito makakamit? Tila muli nating nahaharap ang pag-asam ng pagsulat ng isang hiwalay na pamamaraan para sa bawat uri :/ Paano pa natin ipapaliwanag sa compiler kung ano ang gusto nating mangyari? Ito ay talagang medyo simple! Muli, ang mga wildcard ay tumulong sa amin dito. Ngunit sa pagkakataong ito ay gagamit tayo ng isa pang uri ng wildcard — isang wildcard na may mababang hangganan , na ipinapahayag gamit ang super .
public static void iterateAnimals(Collection<? super Cat> animals) {

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

       System.out.println("Another iteration in the loop!");
   }
}
Narito ang prinsipyo ay magkatulad. Ang <? super Cat>construct ay nagsasabi sa compiler na ang iterateAnimals()pamamaraan ay maaaring tumanggap bilang input ng isang koleksyon ng Catmga bagay o anumang ninuno ng Catklase bilang input. Sa kasong ito, ang Catklase, ang magulang nito Pet, at ang magulang ng magulang nito, Animal, ay tumutugma sa paglalarawang ito. Ang Dogklase ay hindi tumutugma sa aming paghihigpit, kaya ang isang pagtatangka na gamitin ang pamamaraan na may List<Dog>argumento 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);

   // Compilation error!
   iterateAnimals(dogs);
}
Nalutas na namin ang aming problema, at ang mga wildcard ay naging lubhang kapaki-pakinabang :) Sa pamamagitan nito, natapos na ang aralin. Ngayon ay nakita mo na kung gaano kahalaga ang mga generic sa iyong pag-aaral ng Java — mayroon kaming 4 na buong aralin tungkol sa mga ito! Ngunit ngayon ay bihasa ka na sa paksa at maaari mong patunayan ang iyong mga kasanayan sa mga panayam sa trabaho :) At ngayon, oras na upang bumalik sa mga gawain! Pinakamahusay na tagumpay sa iyong pag-aaral! :)
Mga komento
  • Sikat
  • Bago
  • Luma
Dapat kang naka-sign in upang mag-iwan ng komento
Wala pang komento ang page na ito