JavaRush /Java Blogu /Random-AZ /Java Generics-də joker simvollar

Java Generics-də joker simvollar

Qrupda dərc edilmişdir
Salam! Biz generiklər mövzusunu öyrənməyə davam edirik. Əvvəlki mühazirələrdən onlar haqqında kifayət qədər məlumatınız var ( generiklərlə işləyərkən vararqların istifadəsitipin silinməsi haqqında ), lakin biz hələ bir vacib mövzunu əhatə etməmişik: joker işarələr . Bu, generiklərin çox vacib xüsusiyyətidir. O qədər ki, bunun üçün ayrıca mühazirə ayırdıq! Bununla belə, joker simvollarda mürəkkəb bir şey yoxdur, indi bunu görəcəksiniz :) Ümumilikdə joker işarələr - 1Bir nümunəyə baxaq:
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;
   }
}
Burda nə baş verir? Çox oxşar iki vəziyyəti görürük. Bunlardan birincisində biz bir obyekti Stringyazın Object. Bununla bağlı heç bir problem yoxdur, hər şey lazım olduğu kimi işləyir. Amma ikinci vəziyyətdə kompilyator xəta atır. Baxmayaraq ki, biz eyni şeyi edirik deyəsən. Sadəcə, indi biz bir neçə obyektdən ibarət kolleksiyadan istifadə edirik. Bəs səhv niyə baş verir? StringBir obyekti bir növə Objectvə ya 20 obyektə atmağımızın mahiyyətcə fərqi nədir ? Obyekt və obyektlər toplusu arasında mühüm fərq var . Əgər sinif Bbir sinfin davamçısıdırsa А, Collection<B>o, davamçısı deyil Collection<A>. List<String>Məhz bu səbəbdən bizimkini gətirə bilmədik List<Object>. Stringvarisdir Object, lakin List<String>varis deyil List<Object>. İntuitiv olaraq, bu çox məntiqli görünmür. Niyə dilin yaradıcıları bu prinsipi rəhbər tuturdular? Təsəvvür edək ki, kompilyator burada bizə xəta verməyəcək:
List<String> strings = new ArrayList<String>();
List<Object> objects = strings;
Bu vəziyyətdə, məsələn, aşağıdakıları edə bilərik:
objects.add(new Object());
String s = strings.get(0);
Kompilyator bizə səhvlər vermədiyindən və List<Object> objectsətirlər toplusuna istinad yaratmağa imkan verdiyindən, biz sətir deyil, sadəcə olaraq istənilən obyekt stringsəlavə edə bilərik ! Beləliklə, biz yalnız generikdə göstərilən obyektlərin kolleksiyamızda olduğuna dair zəmanəti itirmişik . Yəni biz generiklərin əsas üstünlüyünü - tipli təhlükəsizliyi itirmişik. Və kompilyator bizə bütün bunları etməyə icazə verdiyi üçün, bu o deməkdir ki, biz yalnız proqramın icrası zamanı səhv alacağıq, bu həmişə kompilyasiya xətasından çox daha pisdir. Belə halların qarşısını almaq üçün tərtibçi bizə xəta verir: stringsObjectString
// ошибка компиляции
List<Object> objects = strings;
List<String>...və ona varis olmadığını xatırladır List<Object>. Bu, generiklərin işləməsi üçün sərt bir qaydadır və onlardan istifadə edərkən yadda saxlamaq lazımdır. Gəlin davam edək. Deyək ki, kiçik bir sinif iyerarxiyamız var:
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()");
   }
}
İyerarxiyanın başında sadəcə Heyvanlar dayanır: Ev heyvanları onlardan miras alınır. Ev heyvanları 2 növə bölünür - İtlər və Pişiklər. İndi təsəvvür edin ki, sadə bir üsul yaratmalıyıq iterateAnimals(). Metod istənilən heyvanların kolleksiyasını ( Animal, Pet, , Cat, Dog) qəbul etməli, bütün elementləri təkrarlamalı və hər dəfə konsola nəsə çıxarmalıdır. Bu üsulu yazmağa çalışaq:
public static void iterateAnimals(Collection<Animal> animals) {

   for(Animal animal: animals) {

       System.out.println("Еще один шаг в цикле пройден!");
   }
}
Deyəsən problem həll olundu! Ancaq bu yaxınlarda öyrəndiyimiz kimi, List<Cat>, List<Dog>ya da List<Pet>varis deyilik List<Animal>! Buna görə də, pişiklərin siyahısı olan bir metodu çağırmağa çalışdığımız zaman iterateAnimals()tərtibçi xətası alacağıq:
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);
   }
}
Vəziyyət bizim üçün yaxşı görünmür! Belə çıxır ki, bütün heyvan növlərini sadalamaq üçün ayrıca üsullar yazmalıyıq? Əslində, yox, buna ehtiyac yoxdur :) Və joker işarələr bu işdə bizə kömək edəcək ! Bu konstruksiyadan istifadə edərək problemi bir sadə üsulla həll edəcəyik:
public static void iterateAnimals(Collection<? extends Animal> animals) {

   for(Animal animal: animals) {

       System.out.println("Еще один шаг в цикле пройден!");
   }
}
Bu joker işarədir. Daha doğrusu, bu, bir neçə növ joker simvoldan birincisidir - “ genişdirir ” (başqa bir ad Yuxarı Sərhədli Jokerlərdir ). Bu dizayn bizə nə deyir? Bu o deməkdir ki, metod giriş kimi sinif obyektlərinin Animalvə ya hər hansı nəsil sinfin obyektlərinin toplusunu qəbul edir Animal (? extends Animal). AnimalBaşqa sözlə, metod , Pet, Dogvə ya giriş kimi kolleksiyanı qəbul edə bilər Cat- bunun heç bir fərqi yoxdur. Bunun işlədiyinə əmin olaq:
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);
}
Konsol çıxışı:

Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Biz cəmi 4 kolleksiya və 8 obyekt yaratdıq və konsolda tam olaraq 8 giriş var. Hər şey əla işləyir! :) Wildcard bizə lazımi məntiqi xüsusi növlərə bağlama ilə asanlıqla bir metoda uyğunlaşdırmağa imkan verdi. Hər bir heyvan növü üçün ayrıca üsul yazmaq zərurətindən xilas olduq. Təsəvvür edin ki, tətbiqimiz zooparkda və ya baytarlıq klinikasında istifadə olunsaydı, nə qədər üsulumuz olardı :) İndi isə fərqli vəziyyətə baxaq. Bizim miras iyerarxiyamız eyni qalacaq: yuxarı səviyyəli sinif Animal, bir az aşağıda Ev heyvanları sinfi Petvə növbəti səviyyədə CatDog. İndi metodu yenidən yazmalısınız iretateAnimals()ki, o, itlərdən başqa istənilən heyvan növü ilə işləyə bilsin . Collection<Animal>Yəni qəbul etməli və Collection<Pet>ya giriş olaraq Collection<Cat>, lakin ilə işləməməlidir Collection<Dog>. Buna necə nail ola bilərik? Deyəsən, hər tip üçün ayrıca üsul yazmaq perspektivi yenidən qarşımızda durur :/ Məntiqimizi kompilyatora başqa necə izah edək? Və bunu çox sadə etmək olar! Burada joker simvollar yenidən köməyimizə gələcək. Ancaq bu dəfə fərqli bir növdən istifadə edəcəyik - “ super ” (başqa bir ad Aşağı Sərhədli Joker Kartlardır ).
public static void iterateAnimals(Collection<? super Cat> animals) {

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

       System.out.println("Еще один шаг в цикле пройден!");
   }
}
Buradakı prinsip oxşardır. Konstruksiya <? super Cat>kompilyatora deyir ki, metod iterateAnimals()sinifin Catvə ya hər hansı digər əcdad sinfin obyektlərinin toplusunu giriş kimi qəbul edə bilər Cat. CatBizim vəziyyətimizdə sinfin özü , onun əcdadı Petsvə əcdadın əcdadı bu təsvirə uyğun gəlir Animal. Sinif Dogbu məhdudiyyətə uyğun gəlmir və buna görə də siyahı ilə metoddan istifadə etməyə cəhd List<Dog>kompilyasiya xətası ilə nəticələnəcək:
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);
}
Problemimiz həll olundu və yenə də joker işarələr son dərəcə faydalı oldu :) Bununla mühazirə yekunlaşır. İndi Java öyrənərkən generiklər mövzusunun nə qədər vacib olduğunu görürsünüz - biz bu barədə 4 mühazirə keçirdik! Amma indi siz mövzunu yaxşı başa düşürsünüz və müsahibədə özünüzü sübut edə biləcəksiniz :) İndi isə tapşırıqlara qayıtmağın vaxtıdır! Təhsilinizdə uğurlar! :)
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION