1. Mengusulkan konsep obat generik (mengapa generik dibutuhkan)?
Pertama, mari kita lihat kode pendek berikut:
Public Class Generictest {public static void main (string [] args) {list list = new arraylist (); list.add ("qqyumidi"); list.add ("jagung"); list.add (100); untuk (int i = 0; i <list.size (); i ++) {string name = (string) list.get (i); // 1 System.out.println ("Name:" + Name); }}}Tentukan kumpulan jenis daftar, pertama tambahkan dua nilai jenis string ke dalamnya, dan kemudian tambahkan nilai tipe integer. Ini sepenuhnya diperbolehkan karena jenis daftar default adalah objek saat ini. Dalam loop berikutnya, mudah untuk memiliki kesalahan yang mirip dengan // 1 karena saya lupa menambahkan nilai tipe integer atau alasan pengkodean lain dalam daftar sebelumnya. Karena tahap kompilasi normal, pengecualian "java.lang.classcastException" akan muncul saat runtime. Oleh karena itu, kesalahan seperti itu sulit dideteksi selama proses pengkodean.
Selama proses pengkodean seperti di atas, kami menemukan bahwa ada dua masalah utama:
1. Ketika kami memasukkan objek ke dalam koleksi, koleksi tidak akan mengingat jenis objek ini. Ketika objek ini dikeluarkan dari koleksi lagi, jenis kompilasi dari objek yang diubah menjadi tipe objek, tetapi jenis runtime masih merupakan tipenya sendiri.
2. Oleh karena itu, saat mengeluarkan elemen koleksi pada // 1, tipe yang dipaksakan secara artifisial perlu dikonversi ke tipe target tertentu, dan mudah untuk melihat pengecualian "java.lang.classcastException".
Jadi adakah cara untuk membuat koleksi mengingat berbagai jenis elemen dalam koleksi, dan untuk mencapainya selama tidak ada masalah selama kompilasi, tidak akan ada pengecualian "java.lang.classcastException" selama runtime? Jawabannya adalah menggunakan obat generik.
2. Apa itu generik?
Generik, yaitu, "tipe parameter". Ketika datang ke parameter, hal yang paling akrab adalah memiliki parameter konkret saat mendefinisikan metode, dan kemudian lulus parameter aktual saat memanggil metode ini. Jadi bagaimana Anda memahami jenis parameterisasi? Seperti namanya, itu berarti parameterisasi tipe dari tipe spesifik asli, mirip dengan parameter variabel dalam metode ini. Pada saat ini, tipe juga didefinisikan sebagai bentuk parameter (dapat disebut parameter formal tipe), dan kemudian tipe spesifik (tipe parameter aktual) dilewatkan saat digunakan/dipanggil.
Tampaknya agak rumit. Pertama, mari kita lihat contoh di atas menggunakan penulisan generik.
Public Class Generictest {public static void main (string [] args) { /* list list = new arraylist (); list.add ("qqyumidi"); list.add ("jagung"); list.add (100); */ Daftar <String> Daftar = ArrayList baru <String> (); list.add ("qqyumidi"); list.add ("jagung"); //list.add(100); // 1 Minta kesalahan kompilasi untuk (int i = 0; i <list.size (); i ++) {string name = list.get (i); // 2 System.out.println ("Name:" + Name); }}}Setelah menggunakan penulisan generik, kesalahan kompilasi terjadi ketika Anda ingin menambahkan objek tipe integer di // 1. Melalui Daftar <String>, secara langsung terbatas bahwa hanya elemen tipe string yang dapat terkandung dalam koleksi daftar, jadi tidak perlu melakukan tipe cast at // 2, karena pada saat ini, koleksi dapat mengingat informasi jenis elemen, dan kompiler dapat mengonfirmasi bahwa itu adalah tipe string.
Menggabungkan definisi generik di atas, kita tahu bahwa dalam daftar <string>, String adalah parameter tipe, yaitu, antarmuka daftar yang sesuai harus berisi tipe parameter formal. Selain itu, hasil pengembalian metode get () secara langsung tipe parameter formal ini (yaitu, parameter tipe yang masuk yang sesuai). Mari kita lihat definisi spesifik dari antarmuka daftar:
Daftar Antarmuka Publik <E> memperluas koleksi <E> {int size (); boolean isempty (); Boolean berisi (objek o); Iterator <E> iterator (); Objek [] toArray (); <t> t [] toarray (t [] a); Boolean Add (E E); boolean hapus (objek o); boolean containsAll (collection <?> C); Boolean Addall (koleksi <? Extends e> c); boolean addall (indeks int, koleksi <? extends e> c); Boolean Removeall (Collection <?> C); Boolean Retainall (Collection <?> C); void clear (); boolean sama (objek o); int hashcode (); E get (int index); E set (indeks int, elemen e); void add (indeks int, elemen e); E hapus (indeks int); INT INDEXOF (Objek O); int lastIndexof (objek o); ListIterator <E> listIterator (); ListIterator <E> ListIterator (INT INDEX); Daftar <E> Sublist (int fromIndex, int toIndex);}Kita dapat melihat bahwa setelah definisi generik diadopsi dalam antarmuka daftar, E di <e> mewakili parameter formal tipe, yang dapat menerima parameter tipe tertentu. Dalam definisi antarmuka ini, di mana E muncul, itu berarti bahwa parameter tipe yang sama yang diterima dari luar diterima.
Secara alami, ArrayList adalah kelas implementasi untuk antarmuka daftar, dan formulir definisi adalah:
Dari ini, kami memahami dari perspektif kode sumber mengapa objek tipe integer dikompilasi secara tidak benar pada // 1, dan jenis yang diperoleh pada // 2 secara langsung tipe string.
ArrayList kelas publik <e> memperluas Daftar Abstrak <e> mengimplementasikan <E>, acak, kloning, java.io.serializable {public boolean add (e e) {ensurecapacityinternal (size + 1); // Menambah ModCount !! elementData [size ++] = e; Kembali Benar; } public e get (int index) {rangeCheck (index); checkForComodification (); return arraylist.this.elementData (offset + index); } //...Omit proses definisi spesifik lainnya}3. Kustomisasi Antarmuka Generik, Kelas Generik dan Metode Generik
Dari konten di atas, setiap orang telah memahami proses operasi obat generik yang spesifik. Diketahui juga bahwa antarmuka, kelas, dan metode juga dapat didefinisikan menggunakan obat generik dan digunakan sesuai. Ya, ketika digunakan secara khusus, dapat dibagi menjadi antarmuka generik, kelas generik dan metode generik.
Antarmuka generik khusus, kelas generik dan metode generik mirip dengan daftar dan arraylist dalam kode sumber Java di atas. Sebagai berikut, kita melihat kelas generik dan definisi metode paling sederhana:
Public Class Generictest {public static void main (string [] args) {box <string> name = box baru <string> ("jagung"); System.out.println ("Name:" + name.getData ()); }} kotak kelas <T> {data private t; kotak publik () {} kotak publik (data t) {this.data = data; } public t getData () {data pengembalian; }}Dalam proses mendefinisikan antarmuka generik, kelas generik dan metode generik, parameter umum kita seperti t, e, k, v, dll. Sering digunakan untuk mewakili parameter formal generik karena mereka menerima parameter tipe yang diturunkan dari penggunaan eksternal. Jadi untuk berbagai jenis parameter yang masuk, apakah jenis instance objek yang sesuai dihasilkan sama?
Public Class Generictest {public static void main (string [] args) {box <string> name = box baru <string> ("jagung"); Box <Integer> usia = kotak baru <Integer> (712); System.out.println ("Kelas Nama:" + name.getClass ()); // com.qqyumidi.box system.out.println ("Kelas usia:" + usia.getClass ()); // com.qqyumidi.box system.out.println (name.getClass () == age.getClass ()); // BENAR }}Dari ini, kami menemukan bahwa ketika menggunakan kelas generik, meskipun argumen generik yang berbeda dilewatkan, jenis yang berbeda tidak dihasilkan dalam arti sebenarnya. Hanya ada satu kelas generik yang lewat dalam argumen generik yang berbeda dalam memori, yaitu, masih merupakan tipe asli yang paling dasar (kotak dalam contoh ini). Tentu saja, secara logis, kita dapat memahaminya sebagai beberapa jenis generik yang berbeda.
Alasannya adalah bahwa tujuan konsep obat generik di Java adalah bahwa ia hanya berfungsi dalam tahap kompilasi kode. Selama proses kompilasi, setelah memeriksa hasil generik dengan benar, informasi generik yang relevan akan dihapus. Dengan kata lain, file kelas yang berhasil dikompilasi tidak berisi informasi umum. Informasi generik tidak akan memasuki fase runtime.
Ini dirangkum dalam satu kalimat: Jenis generik secara logis dianggap sebagai banyak jenis yang berbeda, dan sebenarnya tipe dasar yang sama.
Empat. Ketik wildcard
Mengikuti kesimpulan di atas, kita tahu bahwa kotak <number> dan Box <Integer> sebenarnya adalah kedua jenis kotak. Sekarang kita perlu terus mengeksplorasi pertanyaan. Jadi, secara logis, dapatkah kotak <number> dan box <integer> dianggap sebagai tipe generik dengan hubungan orangtua-anak?
Untuk mengklarifikasi masalah ini, mari kita terus lihat contoh berikut:
Public Class Generictest {public static void main (string [] args) {box <number> name = box baru <number> (99); Box <Integer> usia = kotak baru <Integer> (712); getData (nama); // Metode getData (box <number>) dalam tipe generictest is // tidak berlaku untuk argumen (box <integer>) getData (usia); // 1} public static void getData (kotak <number> data) {System.out.println ("Data:" + data.getData ()); }}Kami menemukan bahwa pesan kesalahan muncul di kode // 1: Metode getData (kotak <number>) di t ype generictest tidak berlaku untuk argumen (kotak <Integer>). Jelas, dengan meminta informasi, kami tahu bahwa kotak <number> tidak dapat secara logis dianggap sebagai kelas induk dari kotak <Integer>. Jadi, apa alasannya?
Public Class Generictest {public static void main (string [] args) {box <integer> a = box baru <Integer> (712); Kotak <number> b = a; // 1 kotak <float> f = Kotak baru <float> (3.14f); b.setData (f); // 2} public static void getData (kotak <number> data) {System.out.println ("Data:" + data.getData ()); }} kotak kelas <T> {data private t; kotak publik () {} kotak publik (data t) {setData (data); } public t getData () {data pengembalian; } public void setData (data T) {this.data = data; }}Dalam contoh ini, akan ada pesan kesalahan di // 1 dan // 2. Di sini kita dapat menggunakan metode counter-proof untuk menjelaskannya.
Dengan asumsi bahwa kotak <number> dapat secara logis dianggap sebagai kelas induk dari kotak <Integer>, maka tidak akan ada kesalahan kesalahan pada // 1 dan // 2. Kemudian masalahnya muncul. Jenis apa saat mengambil data melalui metode getData ()? Bilangan bulat? Mengambang? atau nomor? Selain itu, karena urutan yang tidak terkendali dalam proses pemrograman, jenis penilaian harus dilakukan bila perlu dan jenis konversi dilakukan. Jelas, ini bertentangan dengan gagasan generik, jadi secara logis, kotak <number> tidak dapat dianggap sebagai kelas induk kotak <Integer>.
OK, maka mari kita lihat kembali contoh pertama di "Type Wildcards", kita tahu alasan yang lebih dalam untuk permintaan kesalahan spesifiknya. Jadi bagaimana cara menyelesaikannya? Markas besar dapat menentukan fungsi baru. Ini jelas bertentangan dengan konsep polimorfisme di Java, jadi kami membutuhkan jenis referensi yang dapat secara logis digunakan untuk mewakili kelas induk dari kedua kotak <Integer> dan kotak <number>, dan dengan demikian, jenis wildcard muncul.
Jenis wildcard umumnya digunakan sebagai pengganti argumen tipe tertentu. Perhatikan bahwa ini adalah parameter tipe, bukan parameter tipe! Dan Box <?> Secara logis kelas induk dari semua kotak <Integer>, kotak <number> ..., dll. Oleh karena itu, kita masih dapat mendefinisikan metode generik untuk memenuhi persyaratan tersebut.
Public Class Generictest {public static void main (string [] args) {box <string> name = box baru <string> ("jagung"); Box <Integer> usia = kotak baru <Integer> (712); Kotak <number> number = Kotak baru <UmuS> (314); getData (nama); getData (usia); getData (angka); } public static void getData (box <?> Data) {System.out.println ("Data:" + data.getData ()); }}Terkadang, kita juga dapat mendengar tentang jenis wildcards atas dan bawah. Seperti apa sebenarnya itu?
Dalam contoh di atas, jika Anda perlu mendefinisikan metode yang berfungsi mirip dengan getData (), tetapi ada batasan lebih lanjut pada argumen jenis: itu hanya bisa menjadi kelas angka dan subkelasnya. Pada saat ini, batas atas wildcard tipe diperlukan.
Public Class Generictest {public static void main (string [] args) {box <string> name = box baru <string> ("jagung"); Box <Integer> usia = kotak baru <Integer> (712); Kotak <number> number = Kotak baru <UmuS> (314); getData (nama); getData (usia); getData (angka); // getuppernumberdata (nama); // 1 getuppernumberberdata (usia); // 2 getuppernumberdata (angka); // 3} public static void getData (box <?> Data) {System.out.println ("Data:" + data.getData ()); } public static void getuppernumberData (box <? Extends number> data) {System.out.println ("Data:" + data.getData ()); }}Pada titik ini, jelas, panggilan di kode // 1 akan muncul pesan kesalahan, sedangkan panggilan di // 2 // 3 akan normal.
Batas atas wildcard tipe ditentukan oleh bentuk kotak <? memperpanjang nomor>. Sejalan dengan itu, batas bawah wildcard tipe adalah bentuk kotak <? Super Number>, dan artinya persis kebalikan dari batas atas wildcard tipe. Saya tidak akan menjelaskannya terlalu banyak di sini.
5. Bab Ekstra
Contoh -contoh dalam artikel ini terutama dikutip untuk menggambarkan beberapa ide dalam obat generik dan tidak harus memiliki kegunaan praktis. Selain itu, ketika datang ke obat generik, saya percaya bahwa yang paling Anda gunakan adalah dalam koleksi. Bahkan, dalam proses pemrograman yang sebenarnya, Anda dapat menggunakan obat generik untuk menyederhanakan pengembangan dan dapat memastikan kualitas kode dengan baik. Dan satu hal yang perlu diperhatikan adalah bahwa tidak ada apa yang disebut array generik di Java.
Untuk obat generik, yang paling penting adalah memahami ide dan tujuan di belakangnya.
Di atas adalah kompilasi pengetahuan dan informasi tentang java generik. Kami akan terus menambahkan informasi yang relevan di masa mendatang. Terima kasih atas dukungan Anda untuk situs web ini!