Bagaimana caranya dan mencoba menemukan semua subclass dari kelas yang diberikan (atau semua implementator dari antarmuka yang diberikan) di Jawa? Sampai sekarang, saya punya metode untuk melakukan ini, tetapi saya merasa cukup tidak efisien (untuk sedikitnya). Metodenya adalah:
- Dapatkan daftar semua nama kelas yang ada di jalur kelas
- Muat masing-masing kelas dan uji untuk melihat apakah itu adalah subclass atau implementor dari kelas atau antarmuka yang diinginkan
Di Eclipse, ada fitur bagus yang disebut Hirarki Tipe yang berhasil menunjukkan ini dengan cukup efisien. Bagaimana caranya dan melakukannya secara terprogram?
Jawaban:
Tidak ada cara lain untuk melakukannya selain apa yang Anda gambarkan. Pikirkan tentang hal ini - bagaimana bisa ada yang tahu kelas apa yang memperluas ClassX tanpa memindai setiap kelas di classpath?
Eclipse hanya dapat memberi tahu Anda tentang super dan subclass dalam apa yang tampaknya menjadi jumlah waktu "efisien" karena sudah memiliki semua tipe data yang dimuat pada titik di mana Anda menekan tombol "Tampilan dalam Jenis Hirarki" (karena itu adalah terus mengkompilasi kelas Anda, tahu tentang semua yang ada di classpath, dll).
sumber
reflections.getSubTypesOf(aClazz))
tautanMemindai kelas tidak mudah dengan Java murni.
Kerangka kerja pegas menawarkan kelas yang disebut ClassPathScanningCandidateComponentProvider yang dapat melakukan apa yang Anda butuhkan. Contoh berikut akan menemukan semua subclass dari MyClass dalam paket org.example.package
Metode ini memiliki keuntungan tambahan dari menggunakan analisa bytecode untuk menemukan kandidat yang berarti akan tidak memuat semua kelas scan.
sumber
Ini tidak mungkin dilakukan hanya dengan menggunakan Java Reflections API bawaan.
Ada sebuah proyek yang melakukan pemindaian dan pengindeksan classpath Anda yang diperlukan sehingga Anda dapat mengakses informasi ini ...
Refleksi
(Penafian: Saya belum menggunakannya, tetapi deskripsi proyek tampaknya cocok untuk kebutuhan Anda.)
sumber
Jangan lupa bahwa Javadoc yang dihasilkan untuk suatu kelas akan mencakup daftar subkelas yang dikenal (dan untuk antarmuka, kelas implementasi yang dikenal).
sumber
Coba ClassGraph . (Penafian, saya penulisnya). ClassGraph mendukung pemindaian untuk subclass dari kelas yang diberikan, baik pada saat runtime atau saat membangun, tetapi juga banyak lagi. ClassGraph dapat membangun representasi abstrak dari seluruh grafik kelas (semua kelas, anotasi, metode, parameter metode, dan bidang) dalam memori, untuk semua kelas di classpath, atau untuk kelas dalam paket yang dimasukkan dalam daftar putih, dan Anda dapat meminta grafik kelas itu namun kamu ingin. ClassGraph mendukung lebih banyak mekanisme spesifikasi classpath dan classloader daripada pemindai lain, dan juga bekerja dengan mulus dengan sistem modul JPMS baru, jadi jika Anda mendasarkan kode Anda pada ClassGraph, kode Anda akan portabel secara maksimal. Lihat API di sini.
sumber
Saya melakukan ini beberapa tahun yang lalu. Cara yang paling dapat diandalkan untuk melakukan ini (yaitu dengan Java API resmi dan tidak ada dependensi eksternal) adalah dengan menulis dokumen khusus untuk menghasilkan daftar yang dapat dibaca saat runtime.
Anda dapat menjalankannya dari baris perintah seperti ini:
atau jalankan dari semut seperti ini:
Berikut kode dasarnya:
Untuk mempermudah, saya telah menghapus parsing argumen baris perintah dan saya menulis ke System.out daripada file.
sumber
Saya tahu saya terlambat beberapa tahun ke pesta ini, tetapi saya menemukan pertanyaan ini mencoba menyelesaikan masalah yang sama. Anda dapat menggunakan pencarian internal Eclipse secara terprogram, jika Anda menulis Plugin Eclipse (dan dengan demikian mengambil keuntungan dari caching mereka, dll), untuk menemukan kelas yang mengimplementasikan antarmuka. Inilah potongan pertama saya (sangat kasar):
Masalah pertama yang saya lihat sejauh ini adalah bahwa saya hanya menangkap kelas yang secara langsung mengimplementasikan antarmuka, tidak semua subclass mereka - tetapi sedikit rekursi tidak pernah menyakiti siapa pun.
sumber
Dengan mengingat keterbatasan yang disebutkan dalam jawaban lain, Anda juga dapat menggunakan openpojo
PojoClassFactory
( tersedia di Maven ) dengan cara berikut:Di mana
packageRoot
String akar dari paket yang ingin Anda cari (misalnya"com.mycompany"
atau bahkan hanya"com"
), danSuperclass
merupakan supertype Anda (ini juga berfungsi pada antarmuka).sumber
Saya hanya menulis demo sederhana untuk menggunakan
org.reflections.Reflections
untuk mendapatkan subclass dari kelas abstrak:https://github.com/xmeng1/ReflectionsDemo
sumber
Tergantung pada persyaratan khusus Anda, dalam beberapa kasus mekanisme loader layanan Java mungkin mencapai apa yang Anda cari.
Singkatnya, ini memungkinkan pengembang untuk secara eksplisit menyatakan bahwa suatu kelas mensubklasifikasikan beberapa kelas lain (atau mengimplementasikan beberapa antarmuka) dengan mendaftarkannya dalam file di direktori file JAR / WAR
META-INF/services
. Kemudian dapat ditemukan menggunakanjava.util.ServiceLoader
kelas yang, ketika diberiClass
objek, akan menghasilkan instance dari semua subclass yang dideklarasikan dari kelas itu (atau, jikaClass
mewakili antarmuka, semua kelas yang mengimplementasikan antarmuka itu).Keuntungan utama dari pendekatan ini adalah bahwa tidak perlu secara manual memindai seluruh classpath untuk subclass - semua logika penemuan terkandung dalam
ServiceLoader
kelas, dan itu hanya memuat kelas yang secara eksplisit dinyatakan dalamMETA-INF/services
direktori (tidak setiap kelas di classpath) .Namun, ada beberapa kelemahan:
META-INF/services
direktori. Ini merupakan beban tambahan bagi pengembang, dan bisa rawan kesalahan.ServiceLoader.iterator()
menghasilkan contoh subclass, bukan merekaClass
objek. Ini menyebabkan dua masalah:Rupanya Java 9 akan mengatasi beberapa kekurangan ini (khususnya, yang berkaitan dengan instantiasi subkelas).
Sebuah contoh
Misalkan Anda tertarik menemukan kelas yang mengimplementasikan antarmuka
com.example.Example
:Kelas
com.example.ExampleImpl
mengimplementasikan antarmuka itu:Anda akan mendeklarasikan kelas
ExampleImpl
adalah implementasiExample
dengan membuat file yangMETA-INF/services/com.example.Example
berisi tekscom.example.ExampleImpl
.Kemudian, Anda dapat memperoleh instance dari setiap implementasi
Example
(termasuk instance dariExampleImpl
) sebagai berikut:sumber
Perlu dicatat juga bahwa ini tentu saja hanya akan menemukan semua subclass yang ada di classpath Anda saat ini. Agaknya ini tidak apa-apa untuk apa yang Anda lihat saat ini, dan kemungkinan Anda memang mempertimbangkan hal ini, tetapi jika Anda pernah melepaskan non-
final
kelas ke alam bebas (untuk berbagai tingkat "liar") maka sepenuhnya layak bahwa orang lain telah menulis subkelas mereka sendiri yang tidak akan Anda ketahui.Jadi jika Anda ingin melihat semua subclass karena Anda ingin membuat perubahan dan akan melihat bagaimana hal itu mempengaruhi perilaku subclass - maka ingatlah subclass yang tidak bisa Anda lihat. Idealnya semua metode non-pribadi Anda, dan kelas itu sendiri harus didokumentasikan dengan baik; buat perubahan sesuai dengan dokumentasi ini tanpa mengubah semantik metode / bidang non-pribadi dan perubahan Anda harus kompatibel-mundur, untuk setiap subkelas yang setidaknya mengikuti definisi Anda tentang superclass.
sumber
Alasan Anda melihat perbedaan antara implementasi Anda dan Eclipse adalah karena Anda memindai setiap kali, sementara Eclipse (dan alat lainnya) memindai hanya sekali (selama beban proyek sebagian besar waktu) dan membuat indeks. Lain kali Anda meminta data itu tidak memindai lagi, tetapi lihat indeks.
sumber
Saya menggunakan lib refleksi, yang memindai classpath Anda untuk semua subclass: https://github.com/ronmamo/reflections
Beginilah caranya:
sumber
Tambahkan mereka ke peta statis di dalam (this.getClass (). GetName ()) konstruktor kelas induk (atau buat yang default) tetapi ini akan diperbarui dalam runtime. Jika inisialisasi malas adalah suatu pilihan, Anda dapat mencoba pendekatan ini.
sumber
Anda dapat menggunakan perpustakaan org.reflections dan kemudian, membuat objek kelas Refleksi. Menggunakan objek ini, Anda bisa mendapatkan daftar semua subclass dari kelas yang diberikan. https://www.javadoc.io/doc/org.reflections/reflections/0.9.10/org/reflections/Reflections.html
sumber
Saya perlu melakukan ini sebagai test case, untuk melihat apakah kelas baru telah ditambahkan ke kode. Inilah yang saya lakukan
sumber