CodeGym/Java Blog/Acak/Ketik penghapusan
John Squirrels
Level 41
San Francisco

Ketik penghapusan

Dipublikasikan di grup Acak
anggota
Hai! Kami melanjutkan rangkaian pelajaran kami tentang obat generik. Kami sebelumnya mendapat gambaran umum tentang apa itu dan mengapa mereka dibutuhkan. Hari ini kita akan belajar lebih banyak tentang beberapa fitur obat generik dan cara bekerja dengannya. Ayo pergi! Ketik penghapusan - 1Dalam pelajaran terakhir , kita berbicara tentang perbedaan antara tipe generik dan tipe mentah . Tipe mentah adalah kelas generik yang tipenya telah dihapus.
List list = new ArrayList();
Ini sebuah contoh. Di sini kami tidak menunjukkan jenis objek apa yang akan ditempatkan di List. Jika kami mencoba membuat seperti itu Listdan menambahkan beberapa objek ke dalamnya, kami 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 generik hanya muncul di Java 5. Pada saat versi ini dirilis, pemrogram telah menulis banyak kode menggunakan tipe mentah, sehingga fitur bahasa ini tidak dapat berhenti bekerja, dan kemampuan untuk buat tipe mentah di Jawa dipertahankan. Namun, masalahnya ternyata semakin meluas. Seperti yang Anda ketahui, kode Java diubah menjadi format terkompilasi khusus yang disebut bytecode, yang kemudian dijalankan oleh mesin virtual Java. Tetapi jika kita menaruh informasi tentang parameter tipe dalam bytecode selama proses konversi, itu akan merusak semua kode yang ditulis sebelumnya, karena tidak ada parameter tipe sebelum Java 5! Saat bekerja dengan obat generik, ada satu konsep yang sangat penting yang perlu Anda ingat. Ini disebut penghapusan tipe. Ini berarti bahwa kelas tidak berisi informasi tentang parameter tipe. Informasi ini hanya tersedia selama kompilasi dan dihapus (menjadi tidak dapat diakses) sebelum runtime. Jika Anda mencoba memasukkan jenis objek yang salah ke dalam file List<String>, kompiler akan menghasilkan kesalahan. Inilah yang ingin dicapai oleh pembuat bahasa ketika mereka membuat obat generik: pemeriksaan waktu kompilasi. Tapi ketika semua kode Java Anda berubah menjadi bytecode, itu tidak lagi berisi informasi tentang parameter tipe. Dalam bytecode, List<Cat>daftar kucing Anda tidak berbeda dengan List<String>string. Dalam bytecode, tidak ada yang mengatakan bahwa itu catsadalah daftar Catobjek. Informasi tersebut dihapus selama kompilasi — hanya fakta bahwa Anda memiliki List<Object> catsdaftar yang akan berakhir di bytecode program. 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: ini sebenarnya adalah "kumpulan" kecil dari 2 objek, yang langsung disimpan saat objek dibuat. Ini memiliki 2 Tbidang. Saat createAndAdd2Values()metode dijalankan, dua objek yang diteruskan ( Object adan Object bharus dilemparkan ke Ttipe dan kemudian ditambahkan ke TestClassobjek. Dalam main()metode ini, kita membuat a TestClass<Integer>, yaitu Integerargumen tipe menggantikan Integerparameter tipe. Kita juga meneruskan a Doubledan a Stringke metode createAndAdd2Values(). Apakah Anda pikir program kami akan bekerja? Bagaimanapun, kami menetapkan Integersebagai argumen tipe, tetapi Stringpasti tidak dapat dilemparkan ke Integer! Mari kita jalankanmain()metode dan periksa. Keluaran konsol:
22.111
Test String
Itu tidak terduga! Kenapa ini terjadi? Ini adalah hasil dari penghapusan tipe. Informasi tentang Integerargumen tipe yang digunakan untuk membuat instance TestClass<Integer> testobjek kami dihapus saat kode dikompilasi. Lapangan menjadi TestClass<Object> test. DoubleArgumen and kami Stringdengan mudah dikonversi menjadi Objectobjek (mereka tidak dikonversi menjadi Integerobjek seperti yang kami harapkan!) Dan diam-diam ditambahkan ke TestClass. Berikut contoh lain yang sederhana namun sangat mengungkapkan penghapusan tipe:
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());

   }
}
Keluaran konsol:
true
true
Tampaknya kita membuat koleksi dengan tiga jenis argumen yang berbeda — String, Integer, dan kelas kita sendiri Cat. Namun selama konversi ke bytecode, ketiga daftar tersebut menjadi List<Object>, jadi saat program dijalankan, program ini memberi tahu kita bahwa kita menggunakan kelas yang sama di ketiga kasus tersebut.

Ketik erasure saat bekerja dengan array dan generik

Ada poin yang sangat penting yang harus dipahami dengan jelas saat bekerja dengan array dan kelas generik (seperti List). Anda juga harus mempertimbangkannya saat memilih struktur data untuk program Anda. Generik tunduk pada penghapusan jenis. Informasi tentang parameter tipe tidak tersedia saat runtime. Sebaliknya, array mengetahui dan dapat menggunakan informasi tentang tipe datanya saat program sedang berjalan. Mencoba memasukkan tipe yang tidak valid ke dalam array akan menyebabkan pengecualian dilemparkan:
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, mereka mungkin memiliki masalah kompatibilitas. Yang terpenting, Anda tidak dapat membuat larik objek umum atau bahkan hanya larik berparameter. Apakah itu terdengar agak membingungkan? Mari lihat. Misalnya, Anda tidak dapat melakukan semua ini di Jawa:
new List<T>[]
new List<String>[]
new T[]
Jika kami mencoba membuat larik List<String>objek, kami mendapatkan kesalahan kompilasi yang mengeluh tentang pembuatan larik umum:
import java.util.List;

public class Main2 {

   public static void main(String[] args) {

       // Compilation error! Generic array creation
       List<String>[] stringLists = new List<String>[1];
   }
}
Tetapi mengapa ini dilakukan? Mengapa pembuatan array seperti itu tidak diizinkan? Ini semua untuk memberikan keamanan tipe. Jika kompiler membiarkan kita membuat array objek generik seperti itu, kita bisa membuat banyak masalah untuk diri kita sendiri. Berikut adalah 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)
}
Bayangkan membuat array seperti itu List<String>[] stringListsdiperbolehkan dan tidak akan menghasilkan kesalahan kompilasi. Jika ini benar, berikut adalah beberapa hal yang dapat kami lakukan: Pada baris 1, kami membuat larik daftar: List<String>[] stringLists. Array kami berisi satu file List<String>. Pada baris 2, kami membuat daftar angka: List<Integer>. Pada baris 3, kita menugaskan kita List<String>[]ke sebuah Object[] objectsvariabel. Bahasa Java memungkinkan ini: array Xobjek dapat menyimpan Xobjek dan objek dari semua subclass X. Karenanya, Anda dapat memasukkan apa saja ke dalam Objectarray. Pada baris 4, kita mengganti satu-satunya elemen array objects()(a List<String>) dengan a List<Integer>. Jadi, kami memasukkan a List<Integer>ke dalam array yang hanya dimaksudkan untuk disimpanList<String>objek! Kami akan menemukan kesalahan hanya ketika kami mengeksekusi baris 5. A ClassCastExceptionakan dilemparkan saat runtime. Karenanya, larangan pembuatan larik semacam itu ditambahkan ke Jawa. Ini memungkinkan kita menghindari situasi seperti itu.

Bagaimana saya bisa menyiasati penghapusan tipe?

Nah, kita belajar tentang penghapusan tipe. Mari kita coba mengelabui sistem! :) Tugas: Kami memiliki kelas generik TestClass<T>. Kami ingin menulis createNewT()metode untuk kelas ini yang akan membuat dan mengembalikan Tobjek baru. Tapi ini tidak mungkin, kan? Semua informasi tentang Ttipe dihapus selama kompilasi, dan saat runtime kami tidak dapat menentukan tipe objek apa yang perlu kami buat. Sebenarnya ada satu cara rumit untuk melakukan ini. Anda mungkin ingat bahwa Java memiliki Classkelas. Kita dapat menggunakannya untuk menentukan kelas dari salah satu objek kita:
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
Tapi inilah satu aspek yang belum kita bicarakan. Dalam dokumentasi Oracle, Anda akan melihat bahwa kelas Class adalah generik! Ketik penghapusan - 3

https://docs.Oracle.com/javase/8/docs/api/java/lang/Class.html

Dokumentasi mengatakan, "T - tipe kelas yang dimodelkan oleh objek Kelas ini." Menerjemahkan ini dari bahasa dokumentasi ke ucapan biasa, kami memahami bahwa kelas objek Integer.classbukan hanya Class, melainkan Class<Integer>. Jenis objeknya String.classbukan hanya Class, melainkan Class<String>, dll. Jika masih belum jelas, coba tambahkan parameter tipe ke contoh sebelumnya:
public class Main2 {

   public static void main(String[] args) {

       Class<Integer> classInt = Integer.class;
       // Compilation error!
       Class<String> classInt2 = Integer.class;


       Class<String> classString = String.class;
       // Compilation error!
       Class<Double> classString2 = String.class;
   }
}
Dan sekarang, dengan menggunakan pengetahuan ini, kita dapat melewati penghapusan tipe dan menyelesaikan tugas kita! Mari kita coba mendapatkan informasi tentang parameter tipe. Argumen tipe kami adalah MySecretClass:
public class MySecretClass {

   public MySecretClass() {

       System.out.println("A MySecretClass object was created successfully!");
   }
}
Dan 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:
A MySecretClass object was created successfully!
Kami baru saja meneruskan argumen kelas yang diperlukan ke konstruktor kelas generik kami:
TestClass<MySecretClass> testString = new TestClass<>(MySecretClass.class);
Ini memungkinkan kami untuk menyimpan informasi tentang argumen tipe, mencegahnya terhapus seluruhnya. Hasilnya, kami dapat membuat aTobyek! :) Dengan itu, pelajaran hari ini akan segera berakhir. Anda harus selalu mengingat penghapusan jenis saat bekerja dengan obat generik. Solusi ini tampaknya tidak nyaman, tetapi Anda harus memahami bahwa obat generik bukan bagian dari bahasa Java saat dibuat. Fitur ini, yang membantu kami membuat koleksi berparameter dan menangkap kesalahan selama kompilasi, diterapkan nanti. Di beberapa bahasa lain yang menyertakan generik dari versi pertama, tidak ada penghapusan tipe (misalnya, di C#). Omong-omong, kita belum selesai mempelajari obat generik! Dalam pelajaran berikutnya, Anda akan berkenalan dengan beberapa fitur obat generik. Untuk saat ini, sebaiknya selesaikan beberapa tugas! :)
Komentar
  • Populer
  • Baru
  • Lama
Anda harus login untuk memberikan komentar
Halaman ini belum memiliki komentar