JavaRush /Java Blogu /Random-AZ /Silinmə növləri

Silinmə növləri

Qrupda dərc edilmişdir
Salam! Biz generiklərlə bağlı mühazirə silsiləsimizi davam etdiririk. Əvvəllər bunun nə olduğunu və nə üçün lazım olduğunu ümumi şəkildə anladıq. Bu gün biz generiklərin bəzi xüsusiyyətləri haqqında danışacağıq və onlarla işləyərkən bəzi tələlərə baxacağıq. Get! Keçən mühazirədəSilinmə növləri - 1 ümumi tiplərxam növlər arasındakı fərqdən danışdıq . Əgər unutmusunuzsa, Raw Type tipinin silindiyi ümumi sinifdir.
List list = new ArrayList();
Budur bir nümunə. Burada hansı növ obyektlərin bizim List. Birini yaratmağa və ona bəzi obyektlər əlavə etməyə çalışsaq List, IDEa-da bir xəbərdarlıq görəcəyik:

“Unchecked call to add(E) as a member of raw type of java.util.List”.
Amma biz generiklərin yalnız dilin Java 5 versiyasında meydana çıxmasından da danışdıq.O, buraxılan zaman proqramçılar Raw Types-dan istifadə edərək çoxlu kod yazmışdılar və onun fəaliyyətini dayandırmaması üçün, Java-da Raw Tipləri yaratmaq və onlarla işləmək qorunub saxlanılmışdır. Ancaq bu problem daha geniş oldu. Java kodu, bildiyiniz kimi, daha sonra Java virtual maşını tərəfindən yerinə yetirilən xüsusi bayt koduna çevrilir. Və əgər tərcümə prosesi zamanı biz baytkoda parametr tipləri haqqında məlumat yerləşdirsək, o, əvvəllər yazılmış bütün kodu pozardı, çünki Java 5-dən əvvəl heç bir parametr növləri mövcud deyildi! Generiklərlə işləyərkən yadda saxlamağınız lazım olan çox vacib bir xüsusiyyət var. Buna tip silmə deyilir. Onun mahiyyəti ondan ibarətdir ki, onun parametr tipi haqqında heç bir məlumat sinif daxilində saxlanılmır. Bu məlumat yalnız tərtib mərhələsində mövcuddur və icra zamanı silinir (əlçatmaz olur). Səhv tipli bir obyekti List<String>. Dil yaradıcılarının generiklər - tərtib mərhələsində yoxlamalar yaratmaqla nail olduqları məhz budur. Amma yazdığınız bütün Java kodu baytkoda çevrildikdə parametr növləri haqqında məlumat olmayacaq. Bayt kodunun içərisində pişiklərin siyahısı sətirlərdən List<Cat>fərqlənməyəcək List<String>. Bayt kodunda heç nə deməyəcək ki, catsbu obyektlərin siyahısıdır Cat. Kompilyasiya zamanı bu barədə məlumat silinəcək və bayt koduna yalnız proqramınızda müəyyən siyahıya malik olduğunuz məlumat daxil olacaq List<Object> cats. Bunun necə işlədiyini görək:
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 öz ümumi sinifimizi yaratdıq TestClass. Bu olduqca sadədir: mahiyyətcə bu, obyekt yaradılan zaman dərhal orada yerləşdirilən 2 obyektdən ibarət kiçik bir "kolleksiyadır". Sahə kimi 2 obyekti var T. Metod icra edildikdə, iki ötürülən obyekt bizim tipimizə köçürülməlidir createAndAdd2Values(), bundan sonra onlar obyektə əlavə olunacaqlar . Yaratdığımız üsulda , yəni keyfiyyətdə bizdə olacaq . Ancaq eyni zamanda metoda bir ədəd və bir obyekt keçirik . Sizcə proqramımız işləyəcəkmi? Axı biz bir parametr növü kimi göstərdik , lakin onu, əlbəttə ki , ! Gəlin metodu işə salıb yoxlayaq. Konsol çıxışı: 22.111 Test String Gözlənilməz nəticə! Niyə bu baş verdi? Məhz növün silinməsi səbəbindən. Kodun tərtibi zamanı obyektimizin parametr tipi haqqında məlumat silindi. çevrildi . Parametrlərimiz heç bir problem olmadan ( və gözlədiyimiz kimi deyil !) çevrildi və sakitcə . Tipin silinməsinin başqa sadə, lakin çox illüstrativ nümunəsi: 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 çıxışı: true true Belə görünür ki, biz üç müxtəlif parametr tipli kolleksiyalar yaratmışıq - String, Integer, və yaratdığımız sinif Cat. Lakin baytkoda çevrilmə zamanı hər üç siyahı 'ya çevrildi List<Object>, buna görə də icra edildikdə proqram bizə deyir ki, hər üç halda eyni sinifdən istifadə edirik.

Massivlər və generiklərlə işləyərkən silmə yazın

Massivlər və generiklərlə işləyərkən aydın başa düşülməli olan çox vacib bir məqam var (məsələn, List). Proqramınız üçün məlumat strukturu seçərkən də nəzərə almağa dəyər. Generiklər növün silinməsinə məruz qalır. Proqramın icrası zamanı parametr növü haqqında məlumat mövcud deyil. Bunun əksinə olaraq, massivlər proqramın icrası zamanı verilənlərin növü haqqında məlumatları bilir və istifadə edə bilirlər. Massivdə səhv tipli bir dəyər qoymağa çalışmaq bir istisna yaradacaq:
public class Main2 {

   public static void main(String[] args) {

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

Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer
Massivlər və generiklər arasında belə böyük fərq olduğundan, onların uyğunluq problemləri ola bilər. Əvvəla, siz ümumi obyektlər massivi və ya sadəcə yazılmış massiv yarada bilməzsiniz. Bir az qarışıq səslənir? Gəlin daha yaxından nəzər salaq. Məsələn, Java-da bunların heç birini edə bilməzsiniz:
new List<T>[]
new List<String>[]
new T[]
Siyahılar massivi yaratmağa çalışsaq List<String>, ümumi massiv yaratma xətası alırıq:
import java.util.List;

public class Main2 {

   public static void main(String[] args) {

       //ошибка компиляции! Generic array creation
       List<String>[] stringLists = new List<String>[1];
   }
}
Bəs bu niyə edildi? Niyə belə massivlərin yaradılması qadağandır? Bütün bunlar növün təhlükəsizliyini təmin etmək üçündür. Əgər kompilyator bizə ümumi obyektlərdən belə massivlər yaratmağa icazə versəydi, biz çox problemlə üzləşə bilərdik. Joshua Bloch-un “Effektiv Java” kitabından sadə bir nümunə:
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)
}
Təsəvvür edək ki, massiv yaratmağa List<String>[] stringListsicazə veriləcək və kompilyator şikayət etməyəcək. Bu halda biz nə edə bilərik: 1-ci sətirdə biz vərəqlər massivi yaradırıq List<String>[] stringLists. Bizim massivimiz birdən ibarətdir List<String>. 2-ci sətirdə nömrələrin siyahısını yaradırıq List<Integer>. 3-cü sətirdə massivimizi List<String>[]dəyişənə təyin edirik Object[] objects. Java dili bunu etməyə imkan verir: siz həm obyektləri , həm də bütün uşaq siniflərin obyektlərini obyektlər massivinə Xqoya bilərsiniz . Müvafiq olaraq, seriala hər hansı bir şey qoya bilərsiniz. 4-cü sətirdə massivin tək elementini siyahı ilə əvəz edirik . Nəticədə, biz yalnız saxlamaq üçün nəzərdə tutulmuş massivimizə yerləşdirdik ! Yalnız kod 5-ci sətirə çatdıqda xəta ilə qarşılaşacağıq. Proqramın icrası zamanı bir istisna atılacaq . Buna görə də Java dilinə bu cür massivlərin yaradılmasına qadağa qoyuldu - bu, belə halların qarşısını almağa imkan verir. XХObjectsobjects (List<String>)List<Integer>List<Integer>List<String>ClassCastException

Növün silinməsini necə keçə bilərəm?

Yaxşı, biz növün silinməsi haqqında öyrəndik. Gəlin sistemi aldatmağa çalışaq! :) Tapşırıq: Bizim ümumi sinifimiz var TestClass<T>. createNewT()Biz onun içində yeni tipli obyekt yaradacaq və qaytaracaq bir metod yaratmalıyıq Т. Ancaq bunu etmək mümkün deyil, elə deyilmi? Kompilyasiya zamanı tip haqqında bütün məlumatlar Тsilinəcək və proqram işləyərkən biz hansı növ obyekt yaratmalı olduğumuzu öyrənə bilməyəcəyik. Əslində, bir çətin yol var. Yəqin xatırlayırsınız ki, Java-da bir sinif var Class. Bundan istifadə edərək, obyektlərimizdən hər hansı birinin sinfini əldə edə bilərik:
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 çıxışı:

class java.lang.Integer
class java.lang.String
Ancaq burada danışmadığımız bir xüsusiyyət var. Oracle sənədlərində Classın ümumi sinif olduğunu görəcəksiniz! Silinmə növləri - 3Sənədlərdə deyilir: "T bu Class obyekti tərəfindən modelləşdirilmiş sinif növüdür." Bunu sənəd dilindən insan dilinə çevirsək, bu o deməkdir ki, obyekt üçün sinif Integer.classsadəcə Class, lakin Class<Integer>. Obyekt tipi string.classsadəcə Class, Class<String>və s. Hələ də aydın deyilsə, əvvəlki nümunəyə tip parametri əlavə etməyə çalışın:
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;
   }
}
İndi, bu bilikdən istifadə edərək, tipin silinməsini keçə və problemimizi həll edə bilərik! Parametr tipi haqqında məlumat almağa çalışaq. Onun rolunu sinif oynayacaq MySecretClass:
public class MySecretClass {

   public MySecretClass() {

       System.out.println("Объект секретного класса успешно создан!");
   }
}
Həllimizdən praktikada necə istifadə edirik:
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 çıxışı:

Объект секретного класса успешно создан!
Biz sadəcə olaraq tələb olunan sinif parametrini ümumi sinifimizin konstruktoruna ötürdük:
TestClass<MySecretClass> testString = new TestClass<>(MySecretClass.class);
Bunun sayəsində parametr növü haqqında məlumatı saxladıq və onu silinməkdən qoruduq. Nəticədə bir obyekt yarada bildik T! :) Bununla bugünkü mühazirə yekunlaşır. Tipin silinməsi generiklərlə işləyərkən həmişə yadda saxlamaq lazım olan bir şeydir. Bu o qədər də rahat görünmür, ancaq anlamaq lazımdır ki, generiklər Java dili yaradılarkən onun bir hissəsi deyildi. Bu, yığılmış kolleksiyalar yaratmağa və tərtib mərhələsində səhvləri tutmağa kömək edən sonradan əlavə edilmiş xüsusiyyətdir. 1-ci versiyadan bəri generiklərin mövcud olduğu bəzi digər dillərdə tip silmə yoxdur (məsələn, C#). Bununla belə, biz generikləri öyrənməmişik! Növbəti mühazirədə siz onlarla işləməyin daha bir neçə xüsusiyyətləri ilə tanış olacaqsınız. Bu arada bir-iki problemi həll etmək yaxşı olardı! :)
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION