Artikel ini bertujuan untuk memberikan pengantar komprehensif untuk mekanisme refleksi Java. Saya berharap bahwa melalui artikel ini, Anda akan memiliki pemahaman yang komprehensif tentang konten refleksi Java yang relevan.
Sebelum membaca artikel ini, Anda dapat merujuk pada " Pemahaman ulang Java Generik " .
Kata pengantar
Mekanisme refleksi Java adalah fungsi yang sangat kuat. Refleksi dapat dilihat di banyak proyek skala besar seperti Spring dan Mybatis. Melalui mekanisme refleksi, kita dapat memperoleh informasi tipe objek selama operasi. Dengan menggunakan fitur ini, kami dapat menerapkan pola desain seperti mode pabrik dan mode proxy, dan juga dapat memecahkan masalah yang menyusahkan seperti penghapusan generik java. Dalam artikel ini, kami akan menerapkan mekanisme refleksi Java dari perspektif aplikasi praktis.
Dasar refleksi
PS: Artikel ini mengharuskan pembaca untuk memiliki tingkat pemahaman tertentu tentang API mekanisme refleksi. Jika Anda belum pernah terpapar sebelumnya, disarankan untuk melihat awal cepat dari dokumen resmi terlebih dahulu.
Sebelum menerapkan mekanisme refleksi, mari kita lihat bagaimana cara mendapatkan Class refleksi yang sesuai dengan suatu objek. Di Java, kami memiliki tiga cara untuk mendapatkan kelas refleksi suatu objek.
Dengan metode getClass
Di Java, setiap Object memiliki metode getClass . Melalui metode getClass, kita bisa mendapatkan kelas refleksi yang sesuai dari objek ini:
String s = "ziwenxie"; class <?> C = s.getClass ();
Kami juga dapat memanggil forName metode statis dari Class :
Kelas <?> C = class.forname ("java.lang.string"); Atau kita dapat menggunakan .class secara langsung:
Kelas <?> C = string.class;
Pada awal artikel, kami menyebutkan bahwa salah satu manfaat utama refleksi adalah memungkinkan kami untuk mendapatkan informasi jenis objek selama operasi. Mari kita lihat secara rinci dengan contoh.
Pertama, kami membuat antarmuka baru A di bawah paket typeinfo.interfacea :
paket typeInfo.interfacea; antarmuka publik A {void f (); } Kemudian kami membuat antarmuka C baru di dalam paket typeinfo.packageaccess . Antarmuka C mewarisi dari antarmuka A , dan kami juga membuat beberapa metode lain untuk pengujian. Perhatikan bahwa izin dari metode berikut berbeda.
Paket typeInfo.packageAccess; import typeInfo.interfacea.a; kelas C mengimplementasikan A {public void f () {System.out.println ("Public CF ()"); } public void g () {System.out.println ("public cg ()"); } protected void v () {System.out.println ("Protected cv ()"); } void u () {System.out.println ("Paket cu ()"); } private void w () {System.out.println ("private cw ()"); }} public class hiddenc {public static a makea () {return new c (); }} Dalam metode callHiddenMethod() , kami menggunakan beberapa API baru, di mana getDeclaredMethod() digunakan untuk mendapatkan metode yang dirujuk kelas kelas kelas sesuai dengan nama metode, dan kemudian kami dapat memicu metode terkait objek dengan memanggil metode invoke() :
Paket typeInfo; impor typeInfo.interfacea.a; impor typeInfo.packageAccess.hiddenc; import java.lang.reflect.method; kelas publik HiddenImplementation {public static Main (string [] args) melempar pengecualian {a a = hiddenc.makea () (string [) args) melempar pengecualian {a a = hiddenc.makea (); af (); System.out.println (a.getClass (). GetName ()); // Ups! Refleksi masih memungkinkan kita untuk memanggil g (): callhiddenmethod (a, "g"); // dan bahkan metode yang kurang dapat diakses! callhiddenmethod (a, "u"); callhiddenmethod (a, "v"); callhiddenmethod (a, "w"); } static void callhiddenMethod (objek A, string methodName) melempar Exception {Method g = a.getClass (). GetDeclaredMethod (MethodName); g.setAccessible (true); g.invoke (a); }} Dari hasil output, kita dapat melihat bahwa apakah itu adalah metode public , default , protect atau pricate , kita dapat menyebutnya secara bebas melalui kelas refleksi. Tentu saja, kita hanya untuk menunjukkan kekuatan refleksi yang kuat, dan teknik ini tidak direkomendasikan dalam pengembangan aktual.
Publik CF () TypeInfo.PackageAccess.CPublic CG () Paket Cu () Protected CV () Private CW () Private CW ()
Kami memiliki skenario bisnis berikut. Kami memiliki List<Class<? extends Pet>> . Kita perlu menghitung berapa banyak Pet tertentu di kelas koleksi ini. Karena penghapusan generik java, jelas tidak mungkin untuk memperhatikan praktik yang mirip dengan List<? extends Pet> , karena setelah kompiler melakukan pemeriksaan jenis statis, JVM akan memperlakukan semua objek dalam koleksi sebagai Pet selama pelarian, tetapi tidak akan tahu apakah Pet mewakili Cat atau Dog , sehingga informasi jenis objek sebenarnya hilang selama pelarian. PS: Tentang Penghapusan Generik: Saya memiliki penjelasan terperinci dalam artikel sebelumnya. Teman yang tertarik bisa melihatnya.
Untuk mengimplementasikan contoh kami di atas, pertama -tama kami mendefinisikan beberapa kelas:
Pet Public Pet memperluas individu {public pet (name string) {super (name); } public pet () {super (); }} Kelas publik Cat memperluas PET {public cat (name string) {super (name); } public cat () {super (); }} Dog kelas publik memperluas PET {public dog (nama string) {super (name); }} kelas publik Egyptianmau Extends Cat {public EgyptianMau (nama string) {super (name); } public egyptianmau () {super (); }} Public Class Mutt Extends Dog {public mutt (name string) {super (name); } public mutt () {super (); }} Kelas Pet di atas mewarisi dari Individual . Implementasi kelas Individual sedikit lebih rumit. Kami menerapkan antarmuka Comparable dan mendefinisikan kembali aturan perbandingan kelas. Jika kami tidak memahaminya dengan baik, itu tidak masalah. Kami telah mengabstraksi, jadi tidak masalah jika kami tidak memahami prinsip implementasi.
Implementasi Individu kelas publik yang sebanding <PROVENSE> {private static long counter = 0; Private Final Long ID = Counter ++; nama string pribadi; // Nama adalah individu publik opsional (nama string) {this.name = name; } public Individual () {} public String toString () {return getClass (). getsImplename () + (name == null? "": "" + name); } public long id () {return id; } public boolean sama (objek o) {return o instance dari individu && id == ((individual) o) .id; } public int hashCode () {int result = 17; if (name! = null) {result = 37 * result + name.hashCode (); } hasil = 37 * hasil + (int) id; hasil pengembalian; } public int compareto (Individual arg) {// Bandingkan dengan nama kelas pertama: string first = getClass (). getsImplename (); String argfirst = arg.getClass (). GetsImplename (); int firstCompare = first.comppareto (argfirst); if (firstCompare! = 0) {return firstCompare; } if (name! = null && arg.name! = null) {int secondaryCompare = name.compareto (arg.name); if (secendCompare! = 0) {return secondaryCompare; }} return (arg.id <id? -1: (arg.id == id? 0: 1)); }} Di bawah ini adalah PetCreator kelas abstrak. Di masa depan, kita dapat secara langsung mendapatkan koleksi kelas Pet terkait dengan memanggil metode arrayList() . Di sini kami menggunakan metode newInstance() yang tidak kami sebutkan di atas. Ini akan mengembalikan contoh kelas yang benar -benar dimaksud dengan kelas kelas. Apa artinya ini? Misalnya, mendeklarasikan new Dog().getClass().newInstance() dan Direct new Dog() setara.
kelas abstrak publik Petcreator {private acak rand = baru acak (47); // Daftar gettypes yang berbeda dari PET untuk membuat: Daftar abstrak publik <kelas <? memperluas hewan peliharaan >> getTypes (); Public Pet Randompet () {// Buat satu PET acak int n = rand.nextInt (getTypes (). size ()); coba {return getTypes (). get (n) .newInstance (); } catch (InstantiationException e) {lempar runtimeException baru (e); } catch (ilegalAccessException e) {lempar runtimeException baru (e); }} public pet [] createArray (ukuran int) {PET [] hasil = Pet baru [ukuran]; untuk (int i = 0; i <size; i ++) {hasil [i] = randompet (); } hasil pengembalian; } Public ArrayList <Et> ArrayList (int int) {ArrayList <Et> result = ArrayList baru <Et> (); Collections.addall (hasil, createarray (ukuran)); hasil pengembalian; }} Selanjutnya, mari kita terapkan kelas abstrak di atas dan jelaskan kode berikut. Dalam kode berikut, kami mendeklarasikan dua kelas koleksi, allTypes dan types , di antaranya allTypes berisi semua kelas yang dinyatakan di atas, tetapi jenis spesifik kami sebenarnya hanya dua jenis, yaitu Mutt dan EgypianMau , jadi hewan peliharaan yang benar -benar kita butuhkan untuk mendapatkan new hanyalah jenis yang terkandung dalam types . Di masa depan, kita bisa mendapatkan jenis yang terkandung dalam types dengan menelepon getTypes() .
LiteralPreator kelas publik memperluas PetCreator {@suppressWarnings ("Uncecked") Daftar Akhir Statis Publik <class <? memperluas PET >> alltypes = collections.unmodifiblelist (arrays.aslist (Pet.class, dog.class, cat.class, Mutt.class, Egyptianmau.class)); Daftar Akhir Statis Pribadi <Kelas <? memperluas PET >> type = alltypes.sublist (alltypes.indexof (mutt.class), alltypes.size ()); Daftar Publik <Kelas <? Extends Pet >> getTypes () {return tipe; }} Logika keseluruhan telah selesai, dan akhirnya kami menerapkan kelas TypeCounter yang digunakan untuk menghitung jumlah kelas Pet yang relevan dalam set. Jelaskan metode isAssignalbeFrom() , yang dapat menentukan bahwa kelas refleksi adalah subclass atau subclass tidak langsung dari kelas refleksi. Seperti namanya, getSuperclass() adalah untuk mendapatkan kelas induk dari kelas refleksi.
Typecounter kelas publik memperluas hashMap <class <?>, integer> {private class <?> Basetype; typecounter publik (kelas <?> Basetype) {this.basetype = baseType; } public void count (objek obj) {class <?> type = obj.getClass (); if (! Basetype.isassignableFrom (type)) {lempar runtimeException baru (obj + "tipe salah" + type + ", harus tipe atau subtipe" + basetype); } countclass (type); } private void countclass (class <?> type) {integer quantity = get (type); put (type, quantity == null? 1: jumlah + 1); Kelas <?> superclass = type.getSuperclass (); if (superclass! = null && basetype.isassignableFrom (superclass)) {countclass (superclass); }} @Override public String toString () {stringBuilder result = new stringBuilder ("{"); untuk (map.entry <class <?>, integer> pair: entryset ()) {result.append (pair.getKey (). getsImplename ()); result.append ("="); result.append (pair.getValue ()); result.append (","); } result.delete (result.length () - 2, result.length ()); result.append ("}"); return result.toString (); }}Meringkaskan
Di atas adalah semua konten artikel ini tentang contoh berbagi kode dari mekanisme refleksi java, dan saya harap ini akan membantu semua orang. Teman yang tertarik dapat terus merujuk ke situs ini:
Kode Implementasi Penerimaan Penerimaan Belanja Java Pemrograman dan Pencetakan
Penjelasan terperinci tentang implementasi referensi dan proxy dinamis di Java
Pemrograman Java untuk mengimplementasikan berbagi kode sederhana dari Eclipse Lunar
Jika ada kekurangan, silakan tinggalkan pesan untuk menunjukkannya. Terima kasih teman atas dukungan Anda untuk situs ini!