Saya ingin melakukan sesuatu seperti ini:
List<Animal> animals = new ArrayList<Animal>();
for( Class c: list_of_all_classes_available_to_my_app() )
if (c is Animal)
animals.add( new c() );
Jadi, saya ingin melihat semua kelas di alam semesta aplikasi saya, dan ketika saya menemukan satu yang turun dari Animal, saya ingin membuat objek baru dari jenis itu dan menambahkannya ke daftar. Ini memungkinkan saya untuk menambahkan fungsionalitas tanpa harus memperbarui daftar hal-hal. Saya dapat menghindari yang berikut ini:
List<Animal> animals = new ArrayList<Animal>();
animals.add( new Dog() );
animals.add( new Cat() );
animals.add( new Donkey() );
...
Dengan pendekatan di atas, saya cukup membuat kelas baru yang memperluas Animal dan itu akan diambil secara otomatis.
UPDATE: 10/16/2008 9:00 pagi Waktu Standar Pasifik:
Pertanyaan ini telah menghasilkan banyak tanggapan hebat - terima kasih. Dari tanggapan dan penelitian saya, saya telah menemukan bahwa apa yang sebenarnya ingin saya lakukan tidak mungkin dilakukan di Jawa. Ada pendekatan, seperti mekanisme ServiceLoader ddimitrov yang dapat bekerja - tetapi mereka sangat berat untuk apa yang saya inginkan, dan saya percaya saya hanya memindahkan masalah dari kode Java ke file konfigurasi eksternal. Perbarui 5/10/19 (11 tahun kemudian!) Sekarang ada beberapa perpustakaan yang dapat membantu dengan ini menurut jawaban @ IvanNik org.refleksi terlihat bagus. Juga ClassGraph dari jawaban @Luke Hutchison terlihat menarik. Ada beberapa kemungkinan lain dalam jawaban juga.
Cara lain untuk menyatakan apa yang saya inginkan: fungsi statis di kelas Animal saya menemukan dan membuat instance semua kelas yang mewarisi dari Animal - tanpa konfigurasi / coding lebih lanjut. Jika saya harus mengkonfigurasi, saya mungkin juga hanya instantiate di kelas Animal saja. Saya mengerti itu karena program Java hanyalah federasi longgar dari file .class yang memang seperti itu.
Menariknya, sepertinya ini cukup sepele di C #.
sumber
Jawaban:
Saya menggunakan org.refleksi :
Contoh lain:
sumber
animals.add( new c() );
dari kode Anda), karena ketikaClass.newInstance()
ada, Anda tidak memiliki jaminan bahwa kelas spesifik memiliki konstruktor tanpa parameter (C # memungkinkan Anda mengharuskannya dalam generik)Cara Java untuk melakukan apa yang Anda inginkan adalah menggunakan mekanisme ServiceLoader .
Juga banyak orang menggulung sendiri dengan memiliki file di lokasi classpath yang terkenal (yaitu /META-INF/services/myplugin.properties) dan kemudian menggunakan ClassLoader.getResources () untuk menghitung semua file dengan nama ini dari semua guci. Ini memungkinkan setiap toples mengekspor penyedianya sendiri dan Anda dapat membuat instantiate mereka dengan refleksi menggunakan Class.forName ()
sumber
Pikirkan ini dari sudut pandang berorientasi aspek; apa yang ingin Anda lakukan, adalah mengetahui semua kelas saat runtime yang telah memperluas kelas Animal. (Saya pikir itu deskripsi yang sedikit lebih akurat tentang masalah Anda daripada judul Anda; jika tidak, saya tidak berpikir Anda memiliki pertanyaan runtime.)
Jadi apa yang saya pikir Anda inginkan adalah membuat konstruktor dari kelas dasar Anda (Hewan) yang menambah array statis Anda (saya lebih suka ArrayLists, saya sendiri, tetapi untuk masing-masing mereka sendiri) jenis Kelas saat ini yang sedang dipakai.
Jadi, kira-kira;
Tentu saja, Anda memerlukan konstruktor statis pada Animal untuk menginisialisasi instantiatedDerivedClass ... Saya pikir ini akan melakukan apa yang Anda inginkan. Perhatikan bahwa ini tergantung pada jalur eksekusi; jika Anda memiliki kelas Anjing yang berasal dari Hewan yang tidak pernah dipanggil, Anda tidak akan memilikinya dalam daftar Kelas Hewan Anda.
sumber
Sayangnya ini tidak sepenuhnya mungkin karena ClassLoader tidak akan memberi tahu Anda kelas apa yang tersedia. Namun, Anda bisa melakukan sesuatu seperti ini:
Sunting: johnstok benar (dalam komentar) bahwa ini hanya berfungsi untuk aplikasi Java mandiri, dan tidak akan berfungsi di bawah server aplikasi.
sumber
URL[]
tetapi tidak selalu jadi tidak mungkin) dari hierarki ClassLoader. Seringkali meskipun Anda harus memiliki izin karena biasanya Anda akanSecurityManager
dimuat dalam JVM.Anda bisa menggunakan ResolverUtil ( sumber mentah ) dari Stripes Framework
jika Anda membutuhkan sesuatu yang sederhana dan cepat tanpa refactoring kode yang ada.
Berikut adalah contoh sederhana yang tidak memuat kelas apa pun:
Ini juga berfungsi di server aplikasi juga karena di situlah ia dirancang untuk bekerja;)
Pada dasarnya kode melakukan hal berikut:
ClassLoader#loadClass(String fullyQualifiedName)
Animal.class.isAssignableFrom(loadedClass);
sumber
Mekanisme yang paling kuat untuk mendaftar semua subclass dari kelas yang diberikan saat ini adalah ClassGraph , karena menangani array seluas mungkin dari mekanisme spesifikasi classpath , termasuk sistem modul JPMS yang baru. (Akulah penulisnya.)
sumber
Java secara dinamis memuat kelas, jadi semesta kelas Anda hanya yang sudah dimuat (dan belum diturunkan). Mungkin Anda dapat melakukan sesuatu dengan pemuat kelas kustom yang dapat memeriksa supertipe dari setiap kelas yang dimuat. Saya tidak berpikir ada API untuk meminta set kelas yang dimuat.
sumber
Gunakan ini
Edit:
sumber
Terima kasih semua yang menjawab pertanyaan ini.
Sepertinya ini memang kacang yang sulit retak. Saya akhirnya menyerah dan membuat array dan pengambil statis di baseclass saya.
Tampaknya Java tidak diatur untuk dapat ditemukan sendiri seperti halnya C #. Saya kira masalahnya adalah karena aplikasi Java hanya kumpulan file .class di direktori / file jar di suatu tempat, runtime tidak tahu tentang kelas sampai direferensikan. Pada saat itu loader memuatnya - apa yang saya coba lakukan adalah menemukannya sebelum saya referensi yang tidak mungkin tanpa pergi ke sistem file dan mencari.
Saya selalu suka kode yang dapat menemukan sendiri alih-alih saya harus menceritakannya sendiri, tetapi sayangnya ini berhasil juga.
Terima kasih lagi!
sumber
Menggunakan OpenPojo Anda dapat melakukan hal berikut:
sumber
Ini adalah masalah yang sulit dan Anda harus mencari tahu informasi ini menggunakan analisis statis, itu tidak tersedia dengan mudah saat runtime. Pada dasarnya dapatkan classpath dari aplikasi Anda dan pindai seluruh kelas yang tersedia dan baca informasi bytecode dari kelas yang menjadi asal kelasnya. Perhatikan bahwa Anjing kelas mungkin tidak secara langsung mewarisi dari Hewan tetapi mungkin mewarisi dari Pet yang berubah mewarisi dari Hewan, jadi Anda harus melacak hierarki itu.
sumber
Salah satu caranya adalah membuat kelas menggunakan inisialisasi statis ... Saya tidak berpikir ini diwarisi (tidak akan berhasil jika):
Anda harus menambahkan kode ini ke semua kelas yang terlibat. Tapi itu menghindari loop jelek yang besar di suatu tempat, menguji setiap kelas mencari anak-anak Animal.
sumber
Saya memecahkan masalah ini cukup elegan menggunakan Package Level Annotations dan kemudian membuat anotasi itu sebagai argumen daftar kelas.
Temukan kelas Java yang mengimplementasikan antarmuka
Implementasi hanya perlu membuat package-info.java dan memasukkan penjelasan ajaib dengan daftar kelas yang ingin mereka dukung.
sumber