Generik
Batasi elemen dalam koleksi ke jenis tertentu.
istilah
Beberapa catatan:
Tipe parameter dan tipe primitif kompatibel satu sama lain
ArrayList Collection1 = ArrayList baru <Integer> (); // pass, No WarningArrayList <Integer> collection2 = new arraylist (); // pass, ada peringatan
Jenis Parameterisasi Jangan Pertimbangkan Hubungan Warisan Parameter Jenis
ArrayList <String> collection3 = ArrayList baru <Papen> (); // Kompilasi tidak lulus ArrayList <POMPERTIF> COLLECTER4 = NEW ARRAYLIST <String> (); // Kompilasi tidak lewat
Tetapi
ArrayList Collection5 = ArrayList baru <Integer> (); ArrayList <String> collection6 = collection5; // disusun oleh
"?" Wildcard
"?" berarti jenis apa pun. Gunakan "?" Karakter wildcard untuk merujuk ke berbagai jenis parameter. Ini dapat memanggil metode yang tidak terkait dengan parameterisasi (seperti metode size ()), dan tidak dapat memanggil metode yang terkait dengan parameterisasi (seperti metode add ())
Ekstensi Wildcard
Memenuhi syarat batas atas karakter wildcard
ArrayList <? Extends number> collection1 = ArrayList baru <Integer> (); // dikompilasi oleh arraylist <? Extends number> collection2 = new ArrayList <String> (); // dikompilasi oleh tidak
Memenuhi syarat batas bawah karakter wildcard
ArrayList <? Super Integer> collection3 = new ArrayList <number> (); // Compile by ArrayList <? Super Integer> collection4 = new ArrayList <String> (); // dikompilasi oleh tidak
Metode generik khusus
Fungsi Template C ++
Template <class t> t add (t x, t y) {return (t) (x+y);}Java generik pada dasarnya diimplementasikan sepenuhnya dalam kompiler, digunakan oleh kompiler untuk melakukan pemeriksaan jenis dan jenis penilaian, dan kemudian menghasilkan bytecode non-generik biasa. Teknik implementasi ini adalah "penghapusan".
"Hapus" instance
Generik disediakan untuk kompiler Javac untuk menentukan jenis input koleksi. Ketika kompiler mengkompilasi koleksi dengan deskripsi jenis, informasi "jenis" akan dihapus.
Public Class Generictest {public static void main (string [] args) {new generictest (). testType (); } public void testType () {arrayList <Integer> collection1 = ArrayList baru <Integer> (); ArrayList <String> collection2 = ArrayList baru <string> (); System.out.println (collection1.getClass () == collection2.getClass ()); // Jenis kelas dari keduanya sama, yaitu, bytecode adalah sistem yang sama.out.println (collection2.getClass (). GetName ()); // Kelasnya adalah java.util.arraylist, dan tidak ada informasi parameter tipe aktual}}Keluaran
BENAR
java.util.arraylist
Gunakan refleksi untuk melewatkan kompiler dan menambahkan jenis data lainnya ke pengumpulan generik.
Hanya tipe referensi yang dapat digunakan sebagai parameter aktual untuk metode generik:
Public Class Generictest {public static void main (string [] args) {swap (string baru [] {"111", "222"}, 0,1); // dikompilasi oleh // swap (int int [] {1,2}, 0,1); // dikompilasi dengan tidak menyusun karena int bukan tipe referensi swap (integer baru [] {1,2}, 0,1); // dikompilasi dengan}/*Swap elemen i-th dan jth dari array a*/public static <t> void swap (t [] a, int i, int j) {t temp = a [i [i]; a [i] = a [j]; a [j] = temp; }}Tetapi perhatikan bahwa tipe dasar kadang -kadang dapat digunakan sebagai parameter aktual, karena ada pengemasan dan unboxing otomatis. Contoh (disusun dan lulus):
Public Class Generictest {public static void main (string [] args) {new generictest (). testType (); int a = BiggerOne (3,5); // int dan double, dapatkan pertukaran sebagai angka angka B = BigGerOne (3.5.5); // string dan int dapatkan pertukaran sebagai objek objek c = lebih besar ("1", 2); } // return y dari x, y public static <T> t BigGerOne (t x, t y) {return y; }}Pada saat yang sama, contoh ini juga menunjukkan bahwa ketika parameter aktual tidak konsisten, T mengambil persimpangan, yaitu, kelas induk umum pertama. Selain itu, jika Anda menggunakan angka B = BigGerOne (3.5.5); untuk string c = BigGerOne (3.5.5); Kemudian kesalahan kompilasi dilaporkan:
Kesalahan: (17, 29) Java: Tipe yang tidak kompatibel: Jenis yang disimpulkan tidak memenuhi batas batas atas: java.lang.number & java.lang.compparable <? memperluas java.lang.number & java.lang.Compparable <? >>
Batas atas: java.lang.string, java.lang.object
Tapi ada satu hal yang tidak saya mengerti. Saya selangkah demi selangkah debugging dalam ide dan menemukan hasilnya adalah sebagai berikut: Debugging generik Screenshot-1 Saya tidak tahu mengapa B adalah tipe ganda (tetapi akan menyusun dan melaporkan kesalahan saat menerima nilai pengembalian pada double B). Saya tidak tahu apakah itu ada hubungannya dengan IDE. Apakah IDE menampilkan jenis objek ini yang paling akurat saat men -debug?
Ketik inferensi parameter tipe
Proses dimana kompiler menilai parameter tipe aktual dari metode generik disebut tipe inferensi.
Ketika variabel tipe tertentu diterapkan hanya satu dari semua parameter dan nilai pengembalian di seluruh daftar parameter, itu ditentukan berdasarkan jenis aplikasi aktual pada saat itu ketika metode dipanggil. Artinya, jenis parameter generik ditentukan secara langsung berdasarkan jenis parameter atau nilai pengembalian yang dilewati ketika metode dipanggil. Misalnya:
swap (string baru [3], 1,2) -> statis <e> void swap (e [] a, int i, int j)
Ketika variabel tipe diterapkan di beberapa tempat di semua parameter dan nilai pengembalian dari seluruh daftar parameter, jika begitu banyak jenis aplikasi aktual sesuai dengan tipe yang sama saat memanggil metode, jenis parameter generik adalah jenis itu. Misalnya:
Tambahkan (3,5) -> statis <T> T add (t a, t b)
Ketika variabel tipe tertentu diterapkan di banyak tempat di semua parameter dan nilai pengembalian dari seluruh daftar parameter, jika jenis aplikasi aktual di banyak tempat sesuai dengan berbagai jenis saat memanggil metode dan tidak ada nilai pengembalian, jenis persimpangan maksimum di antara beberapa parameter, yaitu, kelas induk biasa pertama. Misalnya:
Isi (bilangan bulat baru [3], 3.5) -> statis <T> void fill (t a [], t v)
Jenis yang sesuai dari contoh ini adalah angka, dikompilasi dan menjalankan masalah.
Ketika variabel tipe diterapkan di beberapa tempat di semua parameter dan nilai pengembalian dari seluruh daftar parameter, jika jenis aplikasi aktual di banyak tempat sesuai dengan berbagai jenis saat memanggil metode, dan ada nilai pengembalian, jenis nilai pengembalian diberikan prioritas, misalnya: misalnya:
int x = add (3,3.5) -> statis <T> t add (t a, t b)
Contoh kesalahan yang dikompilasi di atas, dan tipe x diubah menjadi float juga melaporkan kesalahan, dan perubahan ke angka berhasil.
Contoh inferensi tipe tipe parameter transitif:
Salin (integer baru [5], string baru [5]) -> statis <T> void copy (t [] a, t [] b)
Contoh ini menyimpulkan bahwa tipe parameter aktual adalah objek dan dikompilasi.
Salin (ArrayList baru <string>, integer baru [5]) -> statis <T> void copy (koleksi <T> a, t [] b)
Contoh ini secara langsung menentukan variabel tipe sebagai tipe string berdasarkan instance kelas arraylist parameterisasi, dan melaporkan kesalahan dalam kompilasi.
Kelas generik khusus
contoh
kelas publik genericdao <t> {public void add (t x) {} public t findById (int id) {return null; } public void delete (t obj) {} public void delete (int id) {} public void update (t obj) {} public t findByUserName (nama string) {return null; } public <T> Set <T> findByConditions (String Where) {return null; }}Catatan: Ketika suatu variabel dinyatakan sebagai generik, ia hanya dapat dipanggil dengan variabel dan metode instance (dan tipe tertanam), tetapi tidak dengan variabel statis dan metode statis. Karena anggota statis dibagikan oleh kelas parameter, anggota statis tidak boleh memiliki parameter tipe tingkat kelas.
Perbandingan metode generik dan kelas generik
contoh:
Public Class A <T> () {// Metode anggota kelas generik, T dibatasi oleh T mengikuti t mandorfunc publik () {return null; } // Metode generik, di sini t dan t berbeda dari t kelas A public static <t> t genericfunc (t a) {return null; } public static void main (string [] args) {// dikompilasi tanpa kompilasi // integer i = a <string> (). findByusername ("s"); // dikompilasi dengan set <integer> set = a <string> (). FindByConditions ("s"); }}Di sini integer i = a <string> (). FindByUserName ("s"); akan mengkompilasi dan melaporkan kesalahan:
Kesalahan: (35, 61) Java: Jenis yang tidak kompatibel: java.lang.string tidak dapat dikonversi ke java.lang.integer
Dari contoh ini, dapat dilihat bahwa T dari metode generik dan T kelas A berbeda.
Generik dan refleksi
Dapatkan parameter tipe aktual generik melalui refleksi
Gunakan variabel generik sebagai parameter metode ini, dan gunakan metode getGenericparametertypes dari kelas metode untuk mendapatkan contoh parameter tipe aktual dari generik:
Public Class Generictest {public static void main (String [] args) melempar Exception {getParamType (); } /*Gunakan refleksi untuk memperoleh jenis parameter parameter aktual dari parameter metode* / public static void getParamType () melempar NosuchMethodeException {Method Method = Generictest.class.getMethod ("ApplyMap", MAP.Class); // Dapatkan jenis parameter generik dari tipe metode [] type = method.getGenericparametertypes (); System.out.println (tipe [0]); // Jenis Parameterisasi ParameterizedType PType = (ParameterizedType) Jenis [0]; // tipe primitif System.out.println (ptype.getRawType ()); // jenis parameter tipe aktual.out.println (ptype.getActualTypeARGUMS () [0]); System.out.println (ptype.getActualTypeARGUMS () [1]); } /* Metode untuk menguji jenis parameter* / public static void applymap (peta <integer, string> peta) {}}Hasil output:
java.util.map <java.lang.integer, java.lang.string> antarmuka java.util.mapclass java.lang.integerclass java.lang.string