JavaRush /Java blogi /Random-UZ /Java Genericsdagi joker belgilar

Java Genericsdagi joker belgilar

Guruhda nashr etilgan
Salom! Biz generiklar mavzusini o'rganishni davom ettirmoqdamiz. Siz ular haqida oldingi ma'ruzalardan yaxshi ma'lumotga egasiz ( generiklar bilan ishlashda vararglardan foydalanish va turlarni o'chirish haqida ), lekin biz hali bitta muhim mavzuni ko'rib chiqmadik: joker belgilar . Bu generiklarning juda muhim xususiyati. Shunchaki, biz buning uchun alohida ma'ruza ajratdik! Biroq, joker belgilarda hech qanday murakkab narsa yo'q, buni hozir ko'rasiz :) Umumiy belgilardagi joker belgilar - 1Bir misolni ko'rib chiqamiz:
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;
   }
}
Bu yerda nima bo'lyapti? Biz ikkita juda o'xshash vaziyatni ko'ramiz. Ulardan birinchisida biz ob'ektni yozishga harakat Stringqilyapmiz Object. Bu bilan hech qanday muammo yo'q, hamma narsa kerak bo'lganidek ishlaydi. Ammo ikkinchi holatda kompilyator xatoga yo'l qo'yadi. Garchi biz xuddi shunday qilyapmiz, deb tuyulsa ham. Faqat hozir biz bir nechta ob'ektlar to'plamidan foydalanmoqdamiz. Lekin nima uchun xatolik yuz beradi? StringBitta ob'ektni turga Objectyoki 20 ta ob'ektga quyamizmi, farqi nimada ? Ob'ekt va ob'ektlar to'plami o'rtasida muhim farq bor . Agar sinf Bsinfning vorisi bo'lsa А, Collection<B>u holda vorisi emas Collection<A>. Shuning uchun biz o'zimiznikiga erisha List<String>olmadik List<Object>. Stringmerosxo'r Object, lekin List<String>merosxo'r emas List<Object>. Intuitiv ravishda, bu juda mantiqiy ko'rinmaydi. Nima uchun til ijodkorlari bu tamoyilga amal qilishgan? Tasavvur qilaylik, kompilyator bu erda bizga xato qilmaydi:
List<String> strings = new ArrayList<String>();
List<Object> objects = strings;
Bunday holda, masalan, biz quyidagilarni qilishimiz mumkin:
objects.add(new Object());
String s = strings.get(0);
Kompilyator bizga xato bermagani va List<Object> objectsatrlar to'plamiga havola yaratishga imkon berganligi sababli, biz satrni emas, balki har qanday ob'ektni stringsqo'shishimiz mumkin ! Shunday qilib, biz to'plamimizda faqat umumiy ko'rsatilgan ob'ektlarni o'z ichiga olishi kafolatini yo'qotdik . Ya'ni, biz generiklarning asosiy afzalligi - turdagi xavfsizlikni yo'qotdik. Va kompilyator bizga bularning barchasini bajarishga imkon berganligi sababli, biz faqat dasturni bajarish paytida xatolikka yo'l qo'yishimizni anglatadi, bu har doim kompilyatsiya xatosidan ko'ra yomonroqdir. Bunday holatlarning oldini olish uchun kompilyator bizga xato beradi: stringsObjectString
// ошибка компиляции
List<Object> objects = strings;
List<String>...va unga merosxo'r emasligini eslatadi List<Object>. Bu generiklarning ishlashi uchun temir bilan qoplangan qoida bo'lib, ulardan foydalanishda buni yodda tutish kerak. Keling, davom etaylik. Aytaylik, bizda kichik sinf ierarxiyasi bor:
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()");
   }
}
Ierarxiyaning boshida oddiy hayvonlar turadi: uy hayvonlari ulardan meros bo'lib o'tadi. Uy hayvonlari 2 turga bo'linadi - itlar va mushuklar. Endi biz oddiy usul yaratishimiz kerakligini tasavvur qiling iterateAnimals(). Usul har qanday hayvonlar to'plamini ( Animal, Pet, , Cat, Dog) qabul qilishi, barcha elementlarni takrorlashi va har safar konsolga biror narsa chiqarishi kerak. Keling, ushbu usulni yozishga harakat qilaylik:
public static void iterateAnimals(Collection<Animal> animals) {

   for(Animal animal: animals) {

       System.out.println("Еще один шаг в цикле пройден!");
   }
}
Muammo hal qilinganga o'xshaydi! Biroq, biz yaqinda bilib olganimizdek, List<Cat>yoki merosxo'r List<Dog>emasmiz ! Shuning uchun, biz mushuklar ro'yxati bilan usulni chaqirmoqchi bo'lganimizda , biz kompilyator xatosini olamiz:List<Pet>List<Animal>iterateAnimals()
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);
   }
}
Vaziyat biz uchun yaxshi emas! Ko'rinib turibdiki, biz hayvonlarning barcha turlarini sanab o'tishning alohida usullarini yozishimiz kerak bo'ladi? Aslida, yo'q, kerak emas :) Va joker belgilar bizga bu borada yordam beradi ! Quyidagi konstruksiyadan foydalangan holda muammoni bitta oddiy usulda hal qilamiz:
public static void iterateAnimals(Collection<? extends Animal> animals) {

   for(Animal animal: animals) {

       System.out.println("Еще один шаг в цикле пройден!");
   }
}
Bu joker belgi. Aniqrog'i, bu bir nechta turdagi joker belgilarning birinchisi - “ kengaytiradi ” (boshqa nomi Upper Bounded Wildcards ). Ushbu dizayn bizga nimani aytadi? AnimalBu shuni anglatadiki, usul kirish sifatida sinf ob'ektlari yoki har qanday avlod sinfi ob'ektlari to'plamini oladi Animal (? extends Animal). AnimalBoshqacha qilib aytganda, usul to'plamni , Petyoki kirish sifatida Dogqabul qilishi mumkin Cat- bu hech qanday farq qilmaydi. Bu ishlayotganiga ishonch hosil qilaylik:
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 chiqishi:

Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Еще один шаг в цикле пройден!
Biz jami 4 ta to'plam va 8 ta ob'ekt yaratdik va konsolda atigi 8 ta yozuv mavjud. Hammasi ajoyib ishlaydi! :) Joker belgi bizga ma'lum turlarga bog'langan holda kerakli mantiqni bitta usulga osongina moslashtirish imkonini berdi. Biz hayvonlarning har bir turi uchun alohida usul yozish zaruratidan xalos bo'ldik. Tasavvur qiling-a, agar ilovamiz hayvonot bog'ida yoki veterinariya klinikasida qo'llanilganda qancha usullarga ega bo'lar edik :) Endi boshqa vaziyatni ko'rib chiqamiz. Bizning meros ierarxiyasi bir xil bo'lib qoladi: yuqori darajadagi sinf - Animal, pastda - Uy hayvonlari sinfi Petva keyingi bosqichda - Catva Dog. Endi itlardan tashqariiretateAnimals() har qanday turdagi hayvonlar bilan ishlashi uchun usulni qayta yozishingiz kerak . Ya'ni , yoki kirish sifatida qabul qilinishi kerak , lekin bilan ishlamasligi kerak . Bunga qanday erishishimiz mumkin? Har bir tur uchun alohida usul yozish istiqboli yana oldimizda turganga o'xshaydi :/ Kompilyatorga mantiqimizni yana qanday tushuntirish mumkin? Va buni juda oddiy qilish mumkin! Bu erda joker belgilar yana yordamimizga keladi. Ammo bu safar biz boshqa turdan foydalanamiz - “ super ” (boshqa nomi Pastki chegaralangan joker belgilar ). Collection<Animal>Collection<Pet>Collection<Cat>Collection<Dog>
public static void iterateAnimals(Collection<? super Cat> animals) {

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

       System.out.println("Еще один шаг в цикле пройден!");
   }
}
Bu erda printsip shunga o'xshash. Qurilish <? super Cat>kompilyatorga metod iterateAnimals()kirish sifatida sinf ob'ektlari to'plamini Catyoki boshqa har qanday ajdod sinfini olishi mumkinligini aytadi Cat. CatBizning holatimizda sinfning o'zi , uning ajdodi - Petsva ajdodning ajdodi - bu tavsifga mos keladi Animal. Sinf Dogushbu cheklovga mos kelmaydi va shuning uchun ro'yxat bilan usuldan foydalanishga urinish List<Dog>kompilyatsiya xatosiga olib keladi:
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);
}
Bizning muammomiz hal qilindi va joker belgilar yana juda foydali bo'lib chiqdi :) Shu bilan ma'ruza yakunlanadi. Endi siz Java tilini o'rganishda generiklar mavzusi qanchalik muhimligini ko'rasiz - biz unga 4 ta ma'ruza o'tkazdik! Ammo endi siz mavzuni yaxshi tushundingiz va suhbatda o'zingizni isbotlay olasiz :) Va endi vazifalarga qaytish vaqti keldi! O'qishlaringizga omad! :)
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION