JavaRush /Java blogi /Random-UZ /Java'da generiklar nima

Java'da generiklar nima

Guruhda nashr etilgan
Salom! Bugun biz generiklar haqida gaplashamiz. Aytishim kerakki, siz juda ko'p yangi narsalarni o'rganasiz! Nafaqat bu, balki keyingi bir necha ma'ruzalar ham generiklarga bag'ishlanadi. Java-da generiklar nima - 1 Shuning uchun, agar bu mavzu siz uchun qiziqarli bo'lsa, siz omadlisiz: bugun siz generiklarning xususiyatlari haqida ko'p narsalarni bilib olasiz. Xo'sh, agar bo'lmasa, tinchlaning va dam oling! :) Bu juda muhim mavzu va siz buni bilishingiz kerak. Oddiydan boshlaylik: "nima" va "nima uchun". Jeneriklar nima? Jeneriklar parametrli turlardir. Umumiy yaratishda siz nafaqat uning turini, balki u bilan ishlashi kerak bo'lgan ma'lumotlar turini ham ko'rsatasiz. Menimcha, eng aniq misol allaqachon xayolingizga kelgan - bu ArrayList! Biz uni odatda dasturda qanday yaratamiz:
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<String> myList1 = new ArrayList<>();
       myList1.add("Test String 1");
       myList1.add("Test String 2");
   }
}
Siz taxmin qilganingizdek, ro'yxatning o'ziga xos xususiyati shundaki, unga hamma narsani "to'ldirish" mumkin bo'lmaydi: u faqat ob'ektlar bilan ishlaydi String. Keling, Java tarixiga qisqacha ekskursiya qilaylik va "nima uchun?" Degan savolga javob berishga harakat qilaylik. Buning uchun biz o'zimiz ArrayList sinfining soddalashtirilgan versiyasini yozamiz. Bizning ro'yxat faqat ichki massivga ma'lumotlarni qo'shishi va ushbu ma'lumotlarni olishi mumkin:
public class MyListClass {

   private Object[] data;
   private int count;

   public MyListClass() {
       this.data = new Object[10];
       this.count = 0;
   }

   public void add(Object o) {
       this.data[count] = o;
       count++;
   }

   public Object[] getData() {
       return data;
   }
}
Aytaylik, biz ro'yxatimizda faqat raqamlarni saqlashni xohlaymiz Integer. Bizda generiklar yo'q. IntegerBiz tekshirishning o misolini aniq belgilay olmaymiz add(). Shunda bizning butun sinfimiz faqat uchun mos bo'ladi Integerva biz dunyodagi barcha ma'lumotlar turlari uchun bir xil sinfni yozishimiz kerak bo'ladi! Biz dasturchilarimizga ishonishga qaror qildik va u erda keraksiz narsalarni qo'shmasliklari uchun kodga sharh qoldirishga qaror qildik:
//use it ONLY with Integer data type
public void add(Object o) {
   this.data[count] = o;
   count++;
}
Dasturchilardan biri bu sharhni o'tkazib yubordi va beixtiyor ro'yxatga qatorlar bilan aralashgan raqamlarni qo'yishga harakat qildi va keyin ularning yig'indisini hisoblab chiqdi:
public class Main {

   public static void main(String[] args) {

       MyListClass list = new MyListClass();
       list.add(100);
       list.add(200);
       list.add("Lolkek");
       list.add("Shalala");

       Integer sum1 = (Integer) list.getData()[0] + (Integer) list.getData()[1];
       System.out.println(sum1);

       Integer sum2 = (Integer) list.getData()[2] + (Integer) list.getData()[3];
       System.out.println(sum2);
   }
}
Konsol chiqishi: 300 ta istisno "main" java.lang.ClassCastException: java.lang.Stringni Main.main(Main.java:14) da java.lang.Integerga o'tkazib bo'lmaydi. Bu vaziyatda eng yomoni nima? Dasturchining e'tiborsizligidan yiroq. Eng yomoni, noto'g'ri kod dasturimizda muhim o'rinni egallab, muvaffaqiyatli tuzilgan . Endi biz xatoni kodlash bosqichida emas, balki faqat sinov bosqichida ko'ramiz (va bu eng yaxshi holatda!). Keyinchalik ishlab chiqishda xatolarni tuzatish ancha qimmatga tushadi - ham pul, ham vaqt. Bu aynan generiklarning afzalligi: umumiy sinf omadsiz dasturchiga xatoni darhol aniqlash imkonini beradi. Kod oddiygina kompilyatsiya qilinmaydi!
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Integer> myList1 = new ArrayList<>();

       myList1.add(100);
       myList1.add(100);
       myList1.add("Lolkek");//error!
       myList1.add("Shalala");//error!
   }
}
Dasturchi darhol "o'ziga keladi" va bir zumda o'zini tuzatadi. Aytgancha, Listbunday xatolikni ko'rish uchun o'z sinfimizni yaratishimiz shart emas edi. <Integer>Oddiy ArrayList dan ( ) turdagi qavslarni olib tashlang !
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

      List list = new ArrayList();

      list.add(100);
      list.add(200);
      list.add("Lolkek");
      list.add("Shalala");

       System.out.println((Integer) list.get(0) + (Integer) list.get(1));
       System.out.println((Integer) list.get(2) + (Integer) list.get(3));
   }
}
Konsol chiqishi: 300 ta istisno "main" java.lang.ClassCastException: java.lang.String-ni Main.main(Main.java:16) da java.lang.Integer-ga o'tkazib bo'lmaydi , ya'ni hatto "native" asboblar yordamida ham. Java, siz bu xatoga yo'l qo'yishingiz va xavfli to'plam yaratishingiz mumkin. Однако, если вставить этот code в IDEa, мы увидим предупреждение: “ Unchecked call to add(E) as a member of raw type of java.util.List ” Нам подсказывают, что при добавлении element в коллекцию без дженериков что-то может пойти unday emas. Ammo "xom turdagi" iborasi nimani anglatadi? So'zma-so'z tarjima juda to'g'ri bo'ladi - " xom turdagi " yoki " iflos turdagi ". Raw typeuning turi olib tashlangan umumiy sinfdir. Boshqacha aytganda, List myList1bu Raw type. raw typeBuning aksi , to'g'ri yaratilgan, tip spetsifikatsiyasi bilan generic typeumumiy sinf (sinf sifatida ham tanilgan ). parameterized typeMasalan, List<String> myList1. Sizda savol tug'ilishi mumkin: nega undan foydalanishga ruxsat berilgan raw types? Sababi oddiy. raw typesJava yaratuvchilari muvofiqlik muammolarini yaratmaslik uchun tilda qo'llab-quvvatlashni qoldirdilar . Java 5.0 chiqarilgunga qadar (generiklar ushbu versiyada birinchi marta paydo bo'ldi), juda ko'p kodlar allaqachon yordamida yozilgan edi raw types. Shuning uchun bu imkoniyat bugungi kunda ham mavjud. Biz allaqachon Joshua Bloxning klassik kitobi "Effektiv Java" haqida ma'ruzalarda bir necha bor eslatib o'tgan edik. raw typesTil ijodkorlaridan biri sifatida u kitobda foydalanish va mavzuni e'tibordan chetda qoldirmadi generic types. Java-da generiklar nima - 2Ushbu kitobning 23-bobi juda mazmunli sarlavhaga ega: “Yangi kodda xom turdagilardan foydalanmang.” Buni eslab qolishingiz kerak. Umumiy sinflardan foydalanganda ularni hech qachon generic typega aylantirmang raw type.

Yozilgan usullar

Java sizga umumiy usullarni yaratib, individual usullarni yozish imkonini beradi. Nima uchun bunday usullar qulay? Avvalo, chunki ular har xil turdagi parametrlar bilan ishlashga imkon beradi. Agar bir xil mantiqni har xil turlarga xavfsiz qo'llash mumkin bo'lsa, umumiy usul ajoyib echimdir. Keling, bir misolni ko'rib chiqaylik. Aytaylik, bizda qandaydir ro'yxat bor myList1. Biz undan barcha qiymatlarni olib tashlamoqchimiz va barcha bo'sh joylarni yangi qiymat bilan to'ldirmoqchimiz. Umumiy metodli sinfimiz shunday ko'rinadi:
public class TestClass {

   public static <T> void fill(List<T> list, T val) {
       for (int i = 0; i < list.size(); i++)
           list.set(i, val);
   }

   public static void main(String[] args) {

       List<String> strings = new ArrayList<>();
       strings.add("Старая строка 1");
       strings.add("Старая строка 2");
       strings.add("Старая строка 3");

       fill(strings, "Новая строка");

       System.out.println(strings);

       List<Integer> numbers = new ArrayList<>();
       numbers.add(1);
       numbers.add(2);
       numbers.add(3);

       fill(numbers, 888);
       System.out.println(numbers);
   }
}
Sintaksisga e'tibor bering, u biroz g'ayrioddiy ko'rinadi:
public static <T> void fill(List<T> list, T val)
Qaytish turidan oldin umumiy usulni bildiruvchi <T> belgisi mavjud. Bunda usul kirish sifatida 2 ta parametrni oladi: ob'ektlar ro'yxati T va boshqa alohida ob'ekt T. <T> dan foydalanish orqali usulni terishga erishiladi: biz u erda satrlar ro'yxatini va raqamni o'tkaza olmaymiz. Satrlar va satrlar ro'yxati, raqamlar va raqamlar ro'yxati, bizning ob'ektlarimiz ro'yxati Catva boshqa ob'ekt Cat- bu yagona yo'l. Usul har xil turdagi ma'lumotlar bilan oson ishlashini main()aniq ko'rsatadi . fill()Birinchidan, u kirish sifatida satrlar va satrlar ro'yxatini, so'ngra raqamlar va raqamlar ro'yxatini oladi. Konsol chiqishi: [Newline, Newline, Newline] [888, 888, 888] Tasavvur qiling-a, fill()bizga 30 xil sinf uchun metod mantig'i kerak bo'lsa va bizda umumiy usullar yo'q edi. Biz bir xil usulni 30 marta yozishga majbur bo'lamiz, faqat turli xil ma'lumotlar turlari uchun! Ammo umumiy usullar tufayli biz kodimizni qayta ishlatishimiz mumkin! :)

Yozilgan sinflar

Siz nafaqat Java-da taqdim etilgan umumiy sinflardan foydalanishingiz, balki o'zingiznikini ham yaratishingiz mumkin! Mana oddiy misol:
public class Box<T> {

   private T t;

   public void set(T t) {
       this.t = t;
   }

   public T get() {
       return t;
   }

   public static void main(String[] args) {

       Box<String> stringBox = new Box<>();

       stringBox.set("Старая строка");
       System.out.println(stringBox.get());
       stringBox.set("Новая строка");

       System.out.println(stringBox.get());

       stringBox.set(12345);//ошибка компиляции!
   }
}
Bizning sinfimiz Box<T>("quti") yoziladi. Yaratish paytida unga ma'lumotlar turini ( ) tayinlagan bo'lsak <T>, biz endi unga boshqa turdagi ob'ektlarni joylashtira olmaymiz. Buni misolda ko'rish mumkin. Yaratishda biz ob'ektimiz satrlar bilan ishlashini aniqladik:
Box<String> stringBox = new Box<>();
Va kodning oxirgi qatorida biz 12345 raqamini qutiga qo'yishga harakat qilsak, biz kompilyatsiya xatosini olamiz! Xuddi shunday, biz o'z umumiy sinfimizni yaratdik! :) Shu bilan bugungi ma'ruzamiz yakunlanadi. Ammo biz generiklar bilan xayrlashmayapmiz! Keyingi ma'ruzalarda biz yanada rivojlangan funksiyalar haqida gaplashamiz, shuning uchun xayrlashmang! ) O'qishlaringizga omad! :)
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION