JavaRush /Java Blog /Random-ID /Menghapus tipe

Menghapus tipe

Dipublikasikan di grup Random-ID
Halo! Kami melanjutkan rangkaian kuliah kami tentang obat generik. Sebelumnya , kita telah mengetahui secara umum apa itu dan mengapa itu diperlukan. Hari ini kita akan membahas beberapa fitur obat generik dan melihat beberapa kendala saat menggunakannya. Pergi! Hapus jenis - 1Pada kuliah terakhir, kita telah membahas perbedaan antara Tipe Generik dan Tipe Mentah . Jika Anda lupa, Tipe Mentah adalah kelas generik yang tipenya telah dihapus.
List list = new ArrayList();
Berikut ini contohnya. Di sini kita tidak menentukan jenis objek apa yang akan ditempatkan di List. Jika kita mencoba membuatnya Listdan menambahkan beberapa objek ke dalamnya, kita akan melihat peringatan di IDEa:

“Unchecked call to add(E) as a member of raw type of java.util.List”.
Tetapi kami juga berbicara tentang fakta bahwa obat generik hanya muncul di bahasa versi Java 5. Pada saat dirilis, pemrogram telah menulis banyak kode menggunakan Tipe Mentah, dan agar tidak berhenti bekerja, kemampuan untuk membuat dan bekerja dengan Tipe Mentah di Java dipertahankan. Namun, permasalahan ini ternyata jauh lebih luas. Kode Java, seperti yang Anda ketahui, diubah menjadi bytecode khusus, yang kemudian dieksekusi oleh mesin virtual Java. Dan jika selama proses penerjemahan kita menempatkan informasi tentang tipe parameter ke dalam bytecode, itu akan merusak semua kode yang ditulis sebelumnya, karena sebelum Java 5 tidak ada tipe parameter! Saat bekerja dengan obat generik, ada satu fitur yang sangat penting yang perlu Anda ingat. Ini disebut penghapusan tipe. Esensinya terletak pada kenyataan bahwa tidak ada informasi tentang tipe parameternya yang disimpan di dalam kelas. Informasi ini hanya tersedia pada tahap kompilasi dan dihapus (tidak dapat diakses) pada saat runtime. Jika Anda mencoba memasukkan objek dengan tipe yang salah ke dalam file List<String>, kompiler akan menimbulkan kesalahan. Inilah yang dicapai oleh pembuat bahasa dengan membuat obat generik - pemeriksaan pada tahap kompilasi. Namun ketika semua kode Java yang Anda tulis berubah menjadi bytecode, tidak akan ada informasi tentang tipe parameter. Di dalam bytecode, daftar List<Cat>kucing Anda tidak akan berbeda dengan List<String>string. Tidak ada apa pun dalam bytecode yang mengatakan bahwa catsini adalah daftar objek Cat. Informasi tentang ini akan dihapus selama kompilasi, dan hanya informasi yang Anda miliki dalam daftar tertentu di program Anda yang akan masuk ke kode byte List<Object> cats. Mari kita lihat cara kerjanya:
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();
   }
}
Kami membuat kelas generik kami sendiri TestClass. Ini cukup sederhana: pada dasarnya ini adalah “koleksi” kecil dari 2 objek, yang ditempatkan di sana segera ketika objek tersebut dibuat. Ini memiliki 2 objek sebagai bidang T. Ketika metode ini dijalankan, createAndAdd2Values()dua objek yang diteruskan harus dilemparkan Object ake Object btype kita T, setelah itu mereka akan ditambahkan ke object TestClass. Dalam metode main()yang kita ciptakan TestClass<Integer>, yaitu kualitas Tyang akan kita miliki Integer. Namun pada saat yang sama, createAndAdd2Values()kita meneruskan nomor Doubledan objek ke metode tersebut String. Apakah menurut Anda program kami akan berhasil? Lagi pula, kami menetapkan tipe sebagai parameter Integer, tetapi Stringtentu saja tidak dapat dimasukkan ke Integer! Mari jalankan metodenya main()dan periksa. Output konsol: 22.111 Test String Hasil yang tidak terduga! Kenapa ini terjadi? Justru karena penghapusan tipe. Selama kompilasi kode, informasi tentang tipe parameter Integerobjek kita TestClass<Integer> testtelah dihapus. Dia berubah menjadi TestClass<Object> test. Parameter kami diubah menjadi tanpa masalah Double( dan bukan menjadi , seperti yang kami harapkan!) dan ditambahkan secara diam-diam ke . Berikut contoh penghapusan tipe yang sederhana namun sangat ilustratif: StringObjectIntegerTestClass
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());

   }
}
Output konsol: true true Tampaknya kita telah membuat koleksi dengan tiga tipe parameter berbeda - String, Integer, dan kelas yang kita buat Cat. Namun selama konversi ke bytecode, ketiga daftar berubah menjadi List<Object>, jadi ketika dijalankan, program memberi tahu kita bahwa dalam ketiga kasus tersebut kita menggunakan kelas yang sama.

Ketik penghapusan saat bekerja dengan array dan obat generik

Ada satu hal yang sangat penting yang harus dipahami dengan jelas ketika bekerja dengan array dan generik (misalnya, List). Hal ini juga patut dipertimbangkan ketika memilih struktur data untuk program Anda. Generik dapat mengalami penghapusan tipe. Informasi tentang tipe parameter tidak tersedia selama eksekusi program. Sebaliknya, array mengetahui dan dapat menggunakan informasi tentang tipe datanya selama eksekusi program. Mencoba memasukkan nilai dengan tipe yang salah ke dalam array akan menimbulkan pengecualian:
public class Main2 {

   public static void main(String[] args) {

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

Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer
Karena ada perbedaan besar antara array dan generik, keduanya dapat menimbulkan masalah kompatibilitas. Pertama-tama, Anda tidak dapat membuat array objek generik atau bahkan hanya array yang diketik. Kedengarannya agak membingungkan? Mari kita lihat lebih dekat. Misalnya, Anda tidak dapat melakukan semua ini di Java:
new List<T>[]
new List<String>[]
new T[]
Jika kita mencoba membuat array list List<String>, kita mendapatkan kesalahan kompilasi pembuatan array umum:
import java.util.List;

public class Main2 {

   public static void main(String[] args) {

       //ошибка компиляции! Generic array creation
       List<String>[] stringLists = new List<String>[1];
   }
}
Namun mengapa hal ini dilakukan? Mengapa pembuatan susunan seperti itu dilarang? Ini semua untuk memastikan keamanan tipe. Jika kompiler mengizinkan kita membuat array seperti itu dari objek generik, kita bisa mendapat banyak masalah. Berikut ini contoh sederhana dari buku Joshua Bloch “Effective Java”:
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)
}
Mari kita bayangkan bahwa pembuatan array List<String>[] stringListsdiperbolehkan, dan kompiler tidak akan mengeluh. Inilah yang bisa kita lakukan dalam kasus ini: Pada baris 1, kita membuat array sheet List<String>[] stringLists. Array kami berisi satu List<String>. Pada baris 2 kita membuat daftar nomor List<Integer>. Pada baris 3 kami menetapkan array kami List<String>[]ke variabel Object[] objects. Bahasa Java memungkinkan Anda melakukan ini: XAnda dapat memasukkan objek Xdan objek dari semua kelas anak ke dalam array objek Х. Oleh karena itu, ObjectsAnda dapat memasukkan apa saja ke dalam array. Pada baris 4 kami mengganti elemen tunggal array objects (List<String>)dengan list List<Integer>. Hasilnya, kami menempatkannya List<Integer>di array kami, yang dimaksudkan hanya untuk menyimpan List<String>! Kita akan menemui kesalahan hanya ketika kode mencapai baris 5. Pengecualian akan diberikan selama eksekusi program ClassCastException. Oleh karena itu, larangan pembuatan array seperti itu diperkenalkan ke dalam bahasa Java - ini memungkinkan kita untuk menghindari situasi seperti itu.

Bagaimana cara melewati penghapusan tipe?

Ya, kita telah belajar tentang penghapusan tipe. Mari kita coba menipu sistem! :) Tugas: Kami memiliki kelas generik TestClass<T>. Kita perlu membuat metode di dalamnya createNewT()yang akan membuat dan mengembalikan objek baru bertipe Т. Namun hal ini tidak mungkin dilakukan, bukan? Semua informasi tentang tipenya Тakan terhapus selama kompilasi, dan saat program sedang berjalan, kita tidak akan bisa mengetahui tipe objek apa yang perlu kita buat. Sebenarnya ada satu cara yang rumit. Anda mungkin ingat bahwa ada kelas di Java Class. Dengan menggunakannya, kita bisa mendapatkan kelas dari objek mana pun:
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);
   }
}
Keluaran konsol:

class java.lang.Integer
class java.lang.String
Namun ada satu fitur yang tidak kami bicarakan. Dalam dokumentasi Oracle Anda akan melihat bahwa Kelas adalah kelas generik! Hapus jenis - 3Dokumentasinya mengatakan: “T adalah tipe kelas yang dimodelkan oleh objek Kelas ini.” Jika kita menerjemahkannya dari bahasa dokumentasi ke dalam bahasa manusia, ini berarti bahwa kelas untuk suatu objek Integer.classbukan hanya Class, tetapi Class<Integer>. Tipe objeknya string.classbukan hanya Class, Class<String>, dll. Jika masih kurang jelas, coba tambahkan parameter type pada contoh sebelumnya:
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;
   }
}
Dan sekarang, dengan menggunakan pengetahuan ini, kita dapat melewati penghapusan tipe dan menyelesaikan masalah kita! Mari kita coba mendapatkan informasi tentang tipe parameter. Perannya akan dimainkan oleh kelas MySecretClass:
public class MySecretClass {

   public MySecretClass() {

       System.out.println("Объект секретного класса успешно создан!");
   }
}
Inilah cara kami menggunakan solusi kami dalam praktik:
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();

   }
}
Keluaran konsol:

Объект секретного класса успешно создан!
Kita cukup meneruskan parameter kelas yang diperlukan ke konstruktor kelas generik kita:
TestClass<MySecretClass> testString = new TestClass<>(MySecretClass.class);
Berkat ini, kami menyimpan informasi tentang jenis parameter dan melindunginya agar tidak terhapus. Hasilnya, kami dapat membuat objek T! :) Demikianlah kuliah hari ini berakhir. Penghapusan tipe selalu merupakan sesuatu yang perlu diingat ketika bekerja dengan obat generik. Ini sepertinya tidak nyaman, tetapi Anda perlu memahami bahwa obat generik bukanlah bagian dari bahasa Java saat dibuat. Ini adalah fitur yang ditambahkan kemudian yang membantu kita membuat koleksi yang diketik dan menangkap kesalahan pada tahap kompilasi. Beberapa bahasa lain yang menggunakan bahasa generik sejak versi 1 tidak memiliki penghapusan tipe (misalnya, C#). Namun, kita belum selesai mempelajari obat generik! Dalam kuliah berikutnya Anda akan mengenal beberapa fitur lagi dalam bekerja dengannya. Sementara itu, alangkah baiknya jika kita menyelesaikan beberapa masalah! :)
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION