Membuat Array Generik di Java

1. Perkenalan

Kami mungkin ingin menggunakan array sebagai bagian dari kelas atau fungsi yang mendukung obat generik. Karena cara Java menangani obat generik, ini bisa jadi sulit.

Dalam tutorial ini, kita akan memahami tantangan menggunakan obat generik dengan array. Kemudian, kami akan membuat contoh array generik.

Kami juga akan melihat di mana Java API telah memecahkan masalah serupa.

2. Pertimbangan Saat Menggunakan Array Generik

Perbedaan penting antara array dan generik adalah bagaimana mereka memberlakukan pemeriksaan tipe. Secara khusus, array menyimpan dan memeriksa informasi tipe pada saat runtime. Generik, bagaimanapun, memeriksa kesalahan tipe pada waktu kompilasi dan tidak memiliki informasi tipe pada waktu proses.

Sintaks Java menunjukkan bahwa kita mungkin dapat membuat array umum baru:

T[] elements = new T[size];

Tetapi, jika kami mencoba ini, kami akan mendapatkan kesalahan kompilasi.

Untuk memahami alasannya, mari pertimbangkan hal berikut:

public  T[] getArray(int size) { T[] genericArray = new T[size]; // suppose this is allowed return genericArray; }

Saat tipe T generik tak terikat menyelesaikan ke Objek, metode kami saat runtime akan menjadi:

public Object[] getArray(int size) { Object[] genericArray = new Object[size]; return genericArray; }

Kemudian, jika kita memanggil metode kita dan menyimpan hasilnya dalam array String :

String[] myArray = getArray(5);

Kode akan dikompilasi dengan baik tetapi gagal saat runtime dengan ClassCastException . Ini karena kita baru saja menetapkan Objek [] ke referensi String [] . Secara khusus, cast implisit oleh kompilator akan gagal untuk mengubah Object [] menjadi tipe String [] yang kami butuhkan .

Meskipun kita tidak dapat menginisialisasi array generik secara langsung, masih dimungkinkan untuk mencapai operasi yang setara jika jenis informasi yang tepat disediakan oleh kode pemanggil.

3. Membuat Array Generik

Untuk contoh kita, mari pertimbangkan struktur data tumpukan terbatas MyStack , di mana kapasitas ditetapkan ke ukuran tertentu. Juga, karena kami ingin tumpukan bekerja dengan tipe apa pun, pilihan implementasi yang masuk akal adalah array generik.

Pertama, mari buat bidang untuk menyimpan elemen tumpukan kita, yang merupakan larik umum tipe E :

private E[] elements;

Kedua, mari tambahkan konstruktor:

public MyStack(Class clazz, int capacity) { elements = (E[]) Array.newInstance(clazz, capacity); }

Perhatikan bagaimana kita menggunakan java.lang.reflect.Array # newInstance untuk menginisialisasi array umum kita , yang membutuhkan dua parameter. Parameter pertama menentukan tipe objek di dalam array baru. Parameter kedua menentukan berapa banyak ruang yang harus dibuat untuk array. Sebagai hasil dari Array # newInstance adalah tipe Object , kita perlu mentransmisikannya ke E [] untuk membuat array generik kita.

Kita juga harus memperhatikan konvensi penamaan parameter tipe clazz daripada class, yang merupakan kata khusus di Java.

4. Mempertimbangkan ArrayList

4.1. Menggunakan ArrayList di Place of an Array

Seringkali lebih mudah menggunakan ArrayList generik sebagai pengganti array generik. Mari kita lihat bagaimana kita bisa mengubah MyStack untuk menggunakan ArrayList .

Pertama, mari buat bidang untuk menyimpan elemen kita:

private List elements;

Kedua, dalam konstruktor tumpukan kami, kami dapat menginisialisasi ArrayList dengan kapasitas awal:

elements = new ArrayList(capacity);

Itu membuat kelas kita lebih sederhana, karena kita tidak perlu menggunakan refleksi. Selain itu, kami tidak diharuskan untuk mengirimkan literal kelas saat membuat tumpukan kami. Terakhir, karena kita dapat menyetel kapasitas awal ArrayList , kita bisa mendapatkan manfaat yang sama dengan array.

Oleh karena itu, kita hanya perlu membuat array generik dalam situasi yang jarang terjadi atau saat kita berinteraksi dengan beberapa library eksternal yang memerlukan array.

4.2. Implementasi ArrayList

Menariknya, ArrayList sendiri diimplementasikan menggunakan array generik. Mari intip ke dalam ArrayList untuk melihat caranya.

Pertama, mari kita lihat bidang elemen daftar:

transient Object[] elementData;

Perhatikan ArrayList menggunakan Object sebagai tipe elemen. Karena tipe generik kami tidak diketahui hingga runtime, Object digunakan sebagai superclass tipe apa pun.

Perlu dicatat bahwa hampir semua operasi di ArrayList dapat menggunakan array generik ini karena mereka tidak perlu menyediakan array yang diketik kuat ke dunia luar, kecuali untuk satu metode - toArray !

5. Membangun Array dari Koleksi

5.1. Contoh LinkedList

Mari kita lihat penggunaan larik generik di Java Collections API, tempat kita akan membuat larik baru dari koleksi.

Pertama, mari buat LinkedList baru dengan argumen tipe String dan tambahkan item ke dalamnya:

List items = new LinkedList(); items.add("first item"); items.add("second item"); 

Kedua, mari buat array item yang baru saja kita tambahkan:

String[] itemsAsArray = items.toArray(new String[0]);

Untuk membangun array kita, List . Metode toArray membutuhkan array input. Ia menggunakan array ini murni untuk mendapatkan informasi tipe untuk membuat array kembali dari tipe yang benar.

Dalam contoh kita di atas, kita telah menggunakan String [0] baru sebagai larik masukan kita untuk membangun larik String yang dihasilkan .

5.2. Implementasi LinkedList.toArray

Mari kita intip ke dalam LinkedList.toArray , untuk melihat bagaimana penerapannya di Java JDK.

Pertama, mari kita lihat tanda tangan metode:

public  T[] toArray(T[] a)

Kedua, mari kita lihat bagaimana array baru dibuat saat diperlukan:

a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);

Perhatikan bagaimana ini menggunakan Array # newInstance untuk membangun array baru, seperti dalam contoh tumpukan kami sebelumnya. Juga, perhatikan bagaimana parameter a digunakan untuk menyediakan tipe ke Array # newInstance. Akhirnya, hasil dari Array # newInstance dilemparkan ke T [] membuat array generik.

6. Kesimpulan

Pada artikel ini, pertama-tama kita melihat perbedaan antara array dan generik, diikuti dengan contoh pembuatan array generik. Kemudian, kami menunjukkan bagaimana menggunakan ArrayList mungkin lebih mudah daripada menggunakan array generik. Terakhir, kami juga melihat penggunaan larik generik di Collections API.

Seperti biasa, kode contoh tersedia di GitHub.