Ketik Erasure in Java Explained

1. Ikhtisar

Dalam artikel singkat ini, kita akan membahas dasar-dasar mekanisme penting dalam generik Java yang dikenal sebagai penghapusan tipe.

2. Apa itu Type Erasure?

Penghapusan jenis dapat dijelaskan sebagai proses penerapan batasan jenis hanya pada waktu kompilasi dan membuang informasi jenis elemen pada waktu proses.

Sebagai contoh:

public static  boolean containsElement(E [] elements, E element){ for (E e : elements){ if(e.equals(element)){ return true; } } return false; }

Kompilator menggantikan tipe E tak terikat dengan tipe Objek sebenarnya :

public static boolean containsElement(Object [] elements, Object element){ for (Object e : elements){ if(e.equals(element)){ return true; } } return false; }

Oleh karena itu, kompilator memastikan keamanan tipe kode kita dan mencegah kesalahan runtime.

3. Jenis Jenis Penghapusan

Penghapusan jenis dapat terjadi di tingkat kelas (atau variabel) dan metode.

3.1. Penghapusan Jenis Kelas

Pada tingkat kelas, kompilator membuang parameter tipe pada kelas dan menggantinya dengan ikatan pertamanya, atau Objek jika parameter tipe tidak terikat.

Mari menerapkan Stack menggunakan array:

public class Stack { private E[] stackContent; public Stack(int capacity) { this.stackContent = (E[]) new Object[capacity]; } public void push(E data) { // .. } public E pop() { // .. } }

Setelah kompilasi, compiler mengganti parameter tipe E tak terikat dengan Object :

public class Stack { private Object[] stackContent; public Stack(int capacity) { this.stackContent = (Object[]) new Object[capacity]; } public void push(Object data) { // .. } public Object pop() { // .. } }

Dalam kasus di mana tipe parameter E terikat:

public class BoundStack
    
      { private E[] stackContent; public BoundStack(int capacity) { this.stackContent = (E[]) new Object[capacity]; } public void push(E data) { // .. } public E pop() { // .. } }
    

Compiler akan mengganti parameter tipe terikat E dengan kelas terikat pertama, Comparable dalam hal ini :

public class BoundStack { private Comparable [] stackContent; public BoundStack(int capacity) { this.stackContent = (Comparable[]) new Object[capacity]; } public void push(Comparable data) { // .. } public Comparable pop() { // .. } }

3.2. Metode Jenis Penghapusan

Untuk penghapusan tipe tingkat metode, parameter tipe metode tidak disimpan tetapi dikonversi ke Objek tipe induknya jika tidak terikat atau kelas terikat pertama saat terikat.

Mari pertimbangkan metode untuk menampilkan konten dari setiap array yang diberikan:

public static  void printArray(E[] array) { for (E element : array) { System.out.printf("%s ", element); } }

Setelah kompilasi, compiler mengganti parameter tipe E dengan Object :

public static void printArray(Object[] array) { for (Object element : array) { System.out.printf("%s ", element); } }

Untuk parameter tipe metode terikat:

public static 
    
      void printArray(E[] array) { for (E element : array) { System.out.printf("%s ", element); } }
    

Kita akan menghapus parameter tipe E dan diganti dengan Comparable:

public static void printArray(Comparable[] array) { for (Comparable element : array) { System.out.printf("%s ", element); } }

4. Kasus Tepi

Suatu saat selama proses penghapusan tipe, penyusun membuat metode sintetik untuk membedakan metode serupa. Ini mungkin berasal dari tanda tangan metode yang memperluas kelas terikat pertama yang sama.

Mari buat kelas baru yang memperluas implementasi Stack kita sebelumnya . Harap dicatat ini mengacu pada kelas Stack yang kita buat di bagian 3.1 , dan bukan java.util.Stack .

public class IntegerStack extends Stack { public IntegerStack(int capacity) { super(capacity); } public void push(Integer value) { super.push(value); } }

Sekarang mari kita lihat kode berikut:

IntegerStack integerStack = new IntegerStack(5); Stack stack = integerStack; stack.push("Hello"); Integer data = integerStack.pop();

Setelah penghapusan jenis, kami memiliki:

IntegerStack integerStack = new IntegerStack(5); Stack stack = (IntegerStack) integerStack; stack.push("Hello"); Integer data = (String) integerStack.pop();

Perhatikan bagaimana kita dapat mendorong S tring pada IntegerStack - karena IntegerStack mewarisi push (Object) dari Stack kelas induk . Ini, tentu saja, salah - karena seharusnya bilangan bulat karena integerStack adalah tipe Stack .

Jadi, tidak mengherankan, upaya untuk pop sebuah String dan menetapkan ke Integer menyebabkan ClassCastException dari cast dimasukkan selama dorongan oleh kompilator.

4.1. Metode Jembatan

Untuk mengatasi kasus edge di atas, kompiler terkadang membuat metode jembatan. Ini adalah metode sintetik yang dibuat oleh compiler Java saat mengompilasi kelas atau antarmuka yang memperluas kelas berparameter atau mengimplementasikan antarmuka berparameter di mana tanda tangan metode mungkin sedikit berbeda atau ambigu.

In our example above, the Java compiler preserves polymorphism of generic types after erasure by ensuring no method signature mismatch between IntegerStack‘s push(Integer) method and Stack‘s push(Object) method.

Hence, the compiler creates a bridge method here:

public class IntegerStack extends Stack { // Bridge method generated by the compiler public void push(Object value) { push((Integer)value); } public void push(Integer value) { super.push(value); } }

Consequently, Stack class's push method after type erasure, delegates to the original push method of IntegerStack class.

5. Conclusion

In this tutorial, we've discussed the concept of type erasure with examples in type parameter variables and methods.

You can read more about these concepts:

  • Java Language Specification: Type Erasure
  • The Basics of Java Generics

Seperti biasa, kode sumber yang menyertai artikel ini tersedia di GitHub.