JavaRush /Java blogi /Random-UZ /O'chirish turlari

O'chirish turlari

Guruhda nashr etilgan
Salom! Biz generiklar bo'yicha ma'ruzalarimizni davom ettiramiz. Ilgari biz umumiy ma'noda nima ekanligini va nima uchun kerakligini tushunib oldik. Bugun biz generiklarning ba'zi xususiyatlari haqida gaplashamiz va ular bilan ishlashda ba'zi tuzoqlarni ko'rib chiqamiz. Bor! Oxirgi ma'ruzadaOʻchirish turlari - 1 biz umumiy turlar va xom turdagi o'rtasidagi farq haqida gaplashdik . Agar siz unutgan bo'lsangiz, Raw Type umumiy sinf bo'lib, uning turi o'chirilgan.
List list = new ArrayList();
Mana bir misol. Bu erda biz qaysi turdagi ob'ektlarni bizning joyimizga joylashtirishini aniqlamaymiz List. Agar biz uni yaratishga Listva unga ba'zi ob'ektlar qo'shishga harakat qilsak, IDEa'da ogohlantirishni ko'ramiz:

“Unchecked call to add(E) as a member of raw type of java.util.List”.
Lekin biz generiklar tilning faqat Java 5 versiyasida paydo bo'lganligi haqida ham gapirgan edik.U chiqarilgunga qadar dasturchilar Raw Types yordamida juda ko'p kod yozishgan va u ishlashdan to'xtab qolmasligi uchun Java-da Raw Types yaratish va ular bilan ishlash saqlanib qoldi. Biroq, bu muammo ancha kengroq bo'lib chiqdi. Ma'lumki, Java kodi maxsus baytekodga aylantiriladi, keyinchalik u Java virtual mashinasi tomonidan bajariladi. Va agar tarjima jarayonida biz bayt-kodga parametr turlari haqida ma'lumot joylashtirsak, u avval yozilgan barcha kodlarni buzadi, chunki Java 5 dan oldin hech qanday parametr turlari mavjud emas edi! Jeneriklar bilan ishlashda siz eslab qolishingiz kerak bo'lgan juda muhim xususiyat mavjud. Bu turni o'chirish deb ataladi. Uning mohiyati shundaki, sinf ichida uning parametr turi haqida hech qanday ma'lumot saqlanmaydi. Ushbu ma'lumot faqat kompilyatsiya bosqichida mavjud va ish vaqtida o'chiriladi (ko'rish imkonsiz bo'lib qoladi). Agar siz noto'g'ri turdagi ob'ektni ga qo'yishga harakat qilsangiz List<String>, kompilyator xatoga yo'l qo'yadi. Til yaratuvchilari generiklarni yaratish orqali aynan shunga erishdilar - kompilyatsiya bosqichida tekshiruvlar. Ammo siz yozgan barcha Java kodi bayt kodiga aylanganda, parametr turlari haqida hech qanday ma'lumot bo'lmaydi. Bayt-kod ichida sizning mushuklar ro'yxati satrlardan List<Cat>farq qilmaydi List<String>. catsBayt kodidagi hech narsa bu ob'ektlar ro'yxati deb aytmaydi Cat. Bu haqdagi ma'lumotlar kompilyatsiya paytida o'chiriladi va faqat sizning dasturingizda ma'lum bir ro'yxat mavjud bo'lgan ma'lumotlar bayt kodiga tushadi List<Object> cats. Keling, bu qanday ishlashini ko'rib chiqaylik:
public class TestClass<T> {

   private T value1;
   private T value2;

   public void printValues() {
       System.out.println(value1);
       System.out.println(value2);
   }

   public static <T> TestClass<T> createAndAdd2Values(Object o1, Object o2) {
       TestClass<T> result = new TestClass<>();
       result.value1 = (T) o1;
       result.value2 = (T) o2;
       return result;
   }

   public static void main(String[] args) {
       Double d = 22.111;
       String s = "Test String";
       TestClass<Integer> test = createAndAdd2Values(d, s);
       test.printValues();
   }
}
Biz o'z umumiy sinfimizni yaratdik TestClass. Bu juda oddiy: mohiyatan bu 2 ta ob'ektdan iborat kichik "to'plam" bo'lib, ular ob'ekt yaratilganda darhol joylashtiriladi. U maydon sifatida 2 ta ob'ektga ega T. Usul bajarilganda, o'tkazilgan ikkita ob'ekt bizning turimizga o'tkazilishi createAndAdd2Values()kerak , shundan so'ng ular ob'ektga qo'shiladi . Biz yaratgan usulda , ya'ni sifatda biz bo'lamiz . Shu bilan birga, biz raqam va ob'ektni metodga o'tkazamiz . Sizningcha, bizning dasturimiz ishlaydimi? Axir, biz parametr turi sifatida belgilab qo'ydik , lekin uni, albatta, ga o'tkazib bo'lmaydi ! Keling, usulni ishga tushiramiz va tekshiramiz. Konsol chiqishi: 22.111 Test String Kutilmagan natija! Nima uchun bu sodir bo'ldi? Aynan turdagi o'chirish tufayli. Kodni kompilyatsiya qilish jarayonida ob'ektimizning parametr turi haqidagi ma'lumotlar o'chirildi. U aylandi . Parametrlarimiz hech qanday muammosiz aylantirildi ( biz kutganimizdek emas !) va jimgina ga qo'shildi . Mana yana bir oddiy, ammo juda yorqin misol turini o'chirish: Object aObject bTTestClassmain()TestClass<Integer>TIntegercreateAndAdd2Values()DoubleStringIntegerStringIntegermain()IntegerTestClass<Integer> testTestClass<Object> testDoubleStringObjectIntegerTestClass
import java.util.ArrayList;
import java.util.List;

public class Main {

   private class Cat {

   }

   public static void main(String[] args) {

       List<String> strings = new ArrayList<>();
       List<Integer> numbers = new ArrayList<>();
       List<Cat> cats = new ArrayList<>();

       System.out.println(strings.getClass() == numbers.getClass());
       System.out.println(numbers.getClass() == cats.getClass());

   }
}
Konsol chiqishi: true trueString Biz to'plamlarni uch xil parametr turi - , Integer, va biz yaratgan sinf bilan yaratganga o'xshaymiz Cat. Ammo bayt-kodga o'tkazish paytida barcha uchta ro'yxat ga aylandi List<Object>, shuning uchun bajarilganda dastur bizga uchta holatda ham bir xil sinfdan foydalanayotganimizni aytadi.

Massivlar va generiklar bilan ishlashda erasure yozing

Massivlar va generiklar bilan ishlashda aniq tushunilishi kerak bo'lgan juda muhim nuqta bor (masalan, List). Dasturingiz uchun ma'lumotlar strukturasini tanlashda ham e'tiborga olish kerak. Jeneriklar turi o'chirilishi mumkin. Dasturni bajarish jarayonida parametr turi haqida ma'lumot mavjud emas. Bundan farqli o'laroq, massivlar dasturni bajarish jarayonida o'zlarining ma'lumotlar turi haqidagi ma'lumotlarni biladilar va ulardan foydalanishlari mumkin. Noto'g'ri turdagi qiymatni massivga qo'yishga urinish istisnoga olib keladi:
public class Main2 {

   public static void main(String[] args) {

       Object x[] = new String[3];
       x[0] = new Integer(222);
   }
}
Konsol chiqishi:

Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer
Massivlar va generiklar o'rtasida juda katta farq borligi sababli, ular muvofiqlik muammolariga ega bo'lishi mumkin. Birinchidan, siz umumiy ob'ektlar massivini yoki oddiygina terilgan massivni yarata olmaysiz. Biroz chalkash tuyuladimi? Keling, batafsil ko'rib chiqaylik. Masalan, Java-da bularning hech birini qila olmaysiz:
new List<T>[]
new List<String>[]
new T[]
Agar biz ro'yxatlar qatorini yaratishga harakat qilsak List<String>, biz umumiy massiv yaratish kompilyatsiya xatosini olamiz:
import java.util.List;

public class Main2 {

   public static void main(String[] args) {

       //ошибка компиляции! Generic array creation
       List<String>[] stringLists = new List<String>[1];
   }
}
Lekin nima uchun bu amalga oshirildi? Nima uchun bunday massivlarni yaratish taqiqlangan? Bularning barchasi turdagi xavfsizlikni ta'minlash uchun. Agar kompilyator bizga umumiy ob'ektlardan bunday massivlarni yaratishga ruxsat bergan bo'lsa, biz juda ko'p muammolarga duch kelishimiz mumkin. Joshua Blochning "Effektiv Java" kitobidan oddiy misol:
public static void main(String[] args) {

   List<String>[] stringLists = new List<String>[1];  //  (1)
   List<Integer> intList = Arrays.asList(42, 65, 44);  //  (2)
   Object[] objects = stringLists;  //  (3)
   objects[0] = intList;  //  (4)
   String s = stringLists[0].get(0);  //  (5)
}
Tasavvur qilaylik, massiv yaratishga List<String>[] stringListsruxsat beriladi va kompilyator shikoyat qilmaydi. Bu holatda biz nima qilishimiz mumkin: 1-qatorda biz varaqlar massivini yaratamiz List<String>[] stringLists. Bizning massivimiz bittadan iborat List<String>. 2-qatorda biz raqamlar ro'yxatini yaratamiz List<Integer>. 3-qatorda biz massivimizni List<String>[]o'zgaruvchiga tayinlaymiz Object[] objects. Java tili buni amalga oshirishga imkon beradi: siz ob'ektlar massiviga barcha bolalar sinflarining ob'ektlarini Xham, ob'ektlarini ham qo'yishingiz mumkin . Shunga ko'ra, siz hamma narsani massivga qo'yishingiz mumkin. 4-qatorda massivning bitta elementini ro'yxat bilan almashtiramiz . Natijada, biz faqat saqlash uchun mo'ljallangan massivimizga joylashtirdik ! Kod 5-qatorga yetgandagina xatolikka duch kelamiz. Dasturni bajarish vaqtida istisno qilinadi . Shuning uchun bunday massivlarni yaratishni taqiqlash Java tiliga kiritildi - bu bizga bunday vaziyatlardan qochish imkonini beradi. XХObjectsobjects (List<String>)List<Integer>List<Integer>List<String>ClassCastException

Qanday qilib turni o'chirishni chetlab o'tishim mumkin?

Xo'sh, biz turni o'chirish haqida bilib oldik. Keling, tizimni aldashga harakat qilaylik! :) Vazifa: Bizda umumiy sinf mavjud TestClass<T>. createNewT()Unda yangi turdagi ob'ektni yaratadigan va qaytaradigan usulni yaratishimiz kerak Т. Ammo buni qilish mumkin emas, to'g'rimi? Turga oid barcha ma'lumotlar Тkompilyatsiya paytida o'chiriladi va dastur ishlayotgan paytda biz qanday turdagi ob'ektni yaratishimiz kerakligini aniqlay olmaymiz. Aslida, bitta qiyin yo'l bor. Ehtimol, Java-da sinf borligini eslaysiz Class. Undan foydalanib, biz har qanday ob'ektimizning sinfini olishimiz mumkin:
public class Main2 {

   public static void main(String[] args) {

       Class classInt = Integer.class;
       Class classString = String.class;

       System.out.println(classInt);
       System.out.println(classString);
   }
}
Konsol chiqishi:

class java.lang.Integer
class java.lang.String
Ammo bu erda biz gaplashmagan bir xususiyat bor. Oracle hujjatlarida Class umumiy sinf ekanligini ko'rasiz! Oʻchirish turlari - 3Hujjatlarda shunday deyilgan: "T - bu Class ob'ekti tomonidan modellashtirilgan sinf turi." Agar biz buni hujjat tilidan inson tiliga tarjima qilsak, bu ob'ekt uchun sinf Integer.classshunchaki emas Class, balki Class<Integer>. Ob'ekt turi string.classshunchaki Class, Class<String>va hokazo emas. Agar u hali ham aniq bo'lmasa, oldingi misolga tip parametrini qo'shib ko'ring:
public class Main2 {

   public static void main(String[] args) {

       Class<Integer> classInt = Integer.class;
       //ошибка компиляции!
       Class<String> classInt2 = Integer.class;


       Class<String> classString = String.class;
       //ошибка компиляции!
       Class<Double> classString2 = String.class;
   }
}
Va endi, ushbu bilimdan foydalanib, biz turdagi o'chirishni chetlab o'tishimiz va muammomizni hal qilishimiz mumkin! Keling, parametr turi haqida ma'lumot olishga harakat qilaylik. Uning rolini sinf o'ynaydi MySecretClass:
public class MySecretClass {

   public MySecretClass() {

       System.out.println("Объект секретного класса успешно создан!");
   }
}
Bizning yechimimizdan amalda qanday foydalanamiz:
public class TestClass<T> {

   Class<T> typeParameterClass;

   public TestClass(Class<T> typeParameterClass) {
       this.typeParameterClass = typeParameterClass;
   }

   public T createNewT() throws IllegalAccessException, InstantiationException {
       T t = typeParameterClass.newInstance();
       return t;
   }

   public static void main(String[] args) throws InstantiationException, IllegalAccessException {

       TestClass<MySecretClass> testString = new TestClass<>(MySecretClass.class);
       MySecretClass secret = testString.createNewT();

   }
}
Konsol chiqishi:

Объект секретного класса успешно создан!
Biz shunchaki umumiy sinfimiz konstruktoriga kerakli sinf parametrini o'tkazdik:
TestClass<MySecretClass> testString = new TestClass<>(MySecretClass.class);
Buning yordamida biz parametr turi haqidagi ma'lumotlarni saqlab qoldik va uni o'chirishdan himoya qildik. Natijada, biz ob'ekt yaratishga muvaffaq bo'ldik T! :) Shu bilan bugungi ma'ruza yakunlanadi. Turni o'chirish har doim generiklar bilan ishlashda yodda tutish kerak bo'lgan narsadir. Bu juda qulay ko'rinmaydi, lekin generiklar Java tili yaratilganda uning bir qismi bo'lmaganligini tushunishingiz kerak. Bu bizga terilgan to'plamlarni yaratish va kompilyatsiya bosqichida xatolarni aniqlashga yordam beradigan keyinchalik qo'shilgan xususiyatdir. 1-versiyadan beri generiklar mavjud bo'lgan ba'zi boshqa tillarda turni o'chirish yo'q (masalan, C#). Biroq, biz generiklarni o'rganishni tugatmadik! Keyingi ma'ruzada siz ular bilan ishlashning yana bir qancha xususiyatlari bilan tanishasiz. Bu orada bir-ikki muammoni hal qilsa yaxshi bo'lardi! :)
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION