CodeGym/Blog Java/rawak/Jenis pemadaman
John Squirrels
Tahap
San Francisco

Jenis pemadaman

Diterbitkan dalam kumpulan
Hai! Kami meneruskan siri pelajaran kami tentang generik. Kami sebelum ini mendapat gambaran umum tentang apa itu dan mengapa ia diperlukan. Hari ini kita akan mengetahui lebih lanjut tentang beberapa ciri generik dan tentang bekerja dengannya. Mari pergi! Jenis pemadaman - 1Dalam pelajaran lepas , kita bercakap tentang perbezaan antara jenis generik dan jenis mentah . Jenis mentah ialah kelas generik yang jenisnya telah dialih keluar.
List list = new ArrayList();
Berikut adalah contoh. Di sini kami tidak menunjukkan jenis objek yang akan diletakkan dalam List. Jika kami cuba mencipta Listdan menambah beberapa objek padanya, kami akan melihat amaran dalam IDEA:
"Unchecked call to add(E) as a member of raw type of java.util.List".
Tetapi kami juga bercakap tentang fakta bahawa generik hanya muncul dalam Java 5. Pada masa versi ini dikeluarkan, pengaturcara telah menulis sekumpulan kod menggunakan jenis mentah, jadi ciri bahasa ini tidak dapat berhenti berfungsi, dan keupayaan untuk mencipta jenis mentah di Jawa telah dipelihara. Namun, masalah itu ternyata lebih berleluasa. Seperti yang anda ketahui, kod Java ditukar kepada format tersusun khas yang dipanggil bytecode, yang kemudiannya dilaksanakan oleh mesin maya Java. Tetapi jika kita meletakkan maklumat tentang parameter jenis dalam bytecode semasa proses penukaran, ia akan memecahkan semua kod yang ditulis sebelum ini, kerana tiada parameter jenis sebelum Java 5! Apabila bekerja dengan generik, terdapat satu konsep yang sangat penting yang perlu anda ingat. Ia dipanggil pemadaman jenis. Ini bermakna kelas tidak mengandungi maklumat tentang parameter jenis. Maklumat ini hanya tersedia semasa penyusunan dan dipadamkan (menjadi tidak boleh diakses) sebelum masa jalan. Jika anda cuba meletakkan jenis objek yang salah dalam anda List<String>, pengkompil akan menghasilkan ralat. Inilah sebenarnya yang ingin dicapai oleh pencipta bahasa apabila mereka mencipta generik: semakan masa kompilasi. Tetapi apabila semua kod Java anda bertukar menjadi kod bait, ia tidak lagi mengandungi maklumat tentang parameter jenis. Dalam bytecode, List<Cat>senarai kucing anda tidak berbeza daripada List<String>rentetan. Dalam bytecode, tiada apa yang mengatakan itu catsialah senarai Catobjek. Maklumat sedemikian dipadamkan semasa penyusunan — hanya fakta bahawa anda mempunyai List<Object> catssenarai akan berakhir dalam kod bait program. Mari lihat bagaimana ini berfungsi:
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 mencipta kelas generik kami sendiri TestClass. Ia agak mudah: ia sebenarnya adalah "koleksi" kecil 2 objek, yang disimpan serta-merta apabila objek itu dicipta. Ia mempunyai 2 Tbidang. Apabila createAndAdd2Values()kaedah dilaksanakan, kedua-dua objek yang diluluskan ( Object adan Object bmesti dibuang ke Tjenis dan kemudian ditambah pada TestClassobjek. Dalam main()kaedah tersebut, kami mencipta TestClass<Integer>, iaitu Integerargumen jenis menggantikan Integerparameter jenis. Kami juga menghantar a Doubledan a Stringkepada kaedah createAndAdd2Values(). Adakah anda fikir program kami akan berfungsi? Lagipun, kami menetapkan Integersebagai hujah jenis, tetapi yang Stringpasti tidak boleh dihantar ke Integer! Mari jalankanmain()kaedah dan semak. Output konsol:
22.111
Test String
Itu tidak dijangka! Mengapa ini berlaku? Ia adalah hasil daripada pemadaman jenis. Maklumat tentang Integerjenis hujah yang digunakan untuk membuat seketika TestClass<Integer> testobjek kami telah dipadamkan apabila kod itu disusun. Padang menjadi TestClass<Object> test. DoubleArgumen dan kami Stringmudah ditukar kepada Objectobjek (ia tidak ditukar kepada Integerobjek seperti yang kami jangkakan!) dan ditambah secara senyap-senyap kepada TestClass. Berikut ialah satu lagi contoh pemadaman jenis yang mudah tetapi sangat mendedahkan:
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
Nampaknya kami mencipta koleksi dengan tiga jenis argumen yang berbeza — String, Integer, dan kelas kami sendiri Cat. Tetapi semasa penukaran kepada bytecode, ketiga-tiga senarai menjadi List<Object>, jadi apabila program dijalankan ia memberitahu kami bahawa kami menggunakan kelas yang sama dalam ketiga-tiga kes.

Taip pemadaman apabila bekerja dengan tatasusunan dan generik

Terdapat satu perkara yang sangat penting yang mesti difahami dengan jelas apabila bekerja dengan tatasusunan dan kelas generik (seperti List). Anda juga harus mengambil kiranya apabila memilih struktur data untuk program anda. Generik tertakluk kepada pemadaman jenis. Maklumat tentang parameter jenis tidak tersedia pada masa jalan. Sebaliknya, tatasusunan tahu tentang dan boleh menggunakan maklumat tentang jenis data mereka semasa program berjalan. Percubaan untuk meletakkan jenis yang tidak sah ke dalam tatasusunan akan menyebabkan pengecualian dilemparkan:
public class Main2 {

   public static void main(String[] args) {

       Object x[] = new String[3];
       x[0] = new Integer(222);
   }
}
Output konsol:
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer
Oleh kerana terdapat perbezaan yang begitu besar antara tatasusunan dan generik, mereka mungkin mempunyai masalah keserasian. Di atas semua itu, anda tidak boleh mencipta tatasusunan objek generik atau bahkan tatasusunan berparameter. Adakah itu terdengar agak mengelirukan? Mari kita lihat. Sebagai contoh, anda tidak boleh melakukan apa-apa daripada ini di Jawa:
new List<T>[]
new List<String>[]
new T[]
Jika kami cuba mencipta tatasusunan List<String>objek, kami mendapat ralat kompilasi yang mengadu tentang penciptaan tatasusunan generik:
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? Mengapakah penciptaan tatasusunan sedemikian tidak dibenarkan? Ini semua untuk menyediakan keselamatan jenis. Jika pengkompil membenarkan kami mencipta tatasusunan objek generik sedemikian, kami boleh membuat banyak masalah untuk diri kami sendiri. Berikut ialah contoh mudah 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 bayangkan bahawa membuat tatasusunan seperti List<String>[] stringListsdibenarkan dan tidak akan menghasilkan ralat penyusunan. Jika ini benar, berikut ialah beberapa perkara yang boleh kami lakukan: Dalam baris 1, kami membuat tatasusunan senarai: List<String>[] stringLists. Tatasusunan kami mengandungi satu List<String>. Dalam baris 2, kami membuat senarai nombor: List<Integer>. Dalam baris 3, kami menetapkan kami List<String>[]kepada Object[] objectspembolehubah. Bahasa Java membenarkan ini: tatasusunan Xobjek boleh menyimpan Xobjek dan objek semua subkelas X. Oleh itu, anda boleh meletakkan apa sahaja dalam Objecttatasusunan. Dalam baris 4, kami menggantikan satu-satunya elemen tatasusunan objects()(a List<String>) dengan a List<Integer>. Oleh itu, kami meletakkan List<Integer>dalam tatasusunan yang hanya bertujuan untuk disimpanList<String>objek! Kami akan menghadapi ralat hanya apabila kami melaksanakan baris 5. A ClassCastExceptionakan dilemparkan semasa runtime. Sehubungan itu, larangan terhadap penciptaan tatasusunan sedemikian telah ditambahkan ke Java. Ini membolehkan kita mengelakkan situasi sedemikian.

Bagaimanakah saya boleh mengatasi jenis pemadaman?

Nah, kami belajar tentang pemadaman jenis. Mari cuba menipu sistem! :) Tugasan: Kami mempunyai kelas generik TestClass<T>. Kami ingin menulis createNewT()kaedah untuk kelas ini yang akan mencipta dan mengembalikan Tobjek baharu. Tetapi ini mustahil, bukan? Semua maklumat tentang Tjenis dipadamkan semasa penyusunan, dan pada masa jalan kami tidak dapat menentukan jenis objek yang perlu kami buat. Sebenarnya ada satu cara yang rumit untuk melakukan ini. Anda mungkin ingat bahawa Java mempunyai Classkelas. Kami boleh menggunakannya untuk menentukan kelas mana-mana objek kami:
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);
   }
}
Output konsol:
class java.lang.Integer
class java.lang.String
Tetapi inilah satu aspek yang belum kita bincangkan. Dalam dokumentasi Oracle, anda akan melihat bahawa kelas Kelas adalah generik! Jenis pemadaman - 3

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

Dokumentasi mengatakan, "T - jenis kelas yang dimodelkan oleh objek Kelas ini." Menterjemah ini daripada bahasa dokumentasi kepada pertuturan biasa, kami faham bahawa kelas objek Integer.classbukan hanya Class, tetapi sebaliknya Class<Integer>. Jenis objek String.classbukan sahaja Class, tetapi sebaliknya Class<String>, dsb. Jika masih tidak jelas, cuba tambahkan parameter jenis pada 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, menggunakan pengetahuan ini, kami boleh memintas pemadaman jenis dan menyelesaikan tugas kami! Mari cuba dapatkan maklumat tentang parameter jenis. Argumen jenis kami ialah MySecretClass:
public class MySecretClass {

   public MySecretClass() {

       System.out.println("A MySecretClass object was created successfully!");
   }
}
Dan inilah cara kami menggunakan penyelesaian kami dalam amalan:
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();

   }
}
Output konsol:
A MySecretClass object was created successfully!
Kami baru sahaja menyerahkan hujah kelas yang diperlukan kepada pembina kelas generik kami:
TestClass<MySecretClass> testString = new TestClass<>(MySecretClass.class);
Ini membolehkan kami menyimpan maklumat tentang hujah jenis, menghalangnya daripada dipadamkan sepenuhnya. Hasilnya, kami dapat mencipta aTobjek! :) Dengan itu, pelajaran hari ini berakhir. Anda mesti sentiasa ingat jenis pemadaman apabila bekerja dengan generik. Penyelesaian ini tidak kelihatan sangat mudah, tetapi anda harus memahami bahawa generik bukan sebahagian daripada bahasa Java semasa ia dicipta. Ciri ini, yang membantu kami membuat koleksi berparameter dan menangkap ralat semasa penyusunan, telah dilekatkan kemudian. Dalam beberapa bahasa lain yang menyertakan generik daripada versi pertama, tiada jenis pemadaman (contohnya, dalam C#). Ngomong-ngomong, kami belum selesai mempelajari generik! Dalam pelajaran seterusnya, anda akan berkenalan dengan beberapa lagi ciri generik. Buat masa ini, adalah baik untuk menyelesaikan beberapa tugas! :)
Komen
  • Popular
  • Baru
  • Tua
Anda mesti log masuk untuk meninggalkan ulasan
Halaman ini tidak mempunyai sebarang ulasan lagi