Chcę zrobić coś takiego:
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() );
Chcę więc przyjrzeć się wszystkim klasom we wszechświecie mojej aplikacji, a kiedy znajdę taką, która pochodzi od Animal, chcę utworzyć nowy obiekt tego typu i dodać go do listy. Dzięki temu mogę dodawać funkcje bez konieczności aktualizowania listy rzeczy. Mogę uniknąć następujących rzeczy:
List<Animal> animals = new ArrayList<Animal>();
animals.add( new Dog() );
animals.add( new Cat() );
animals.add( new Donkey() );
...
Dzięki powyższemu podejściu mogę po prostu utworzyć nową klasę, która rozszerza Animal i zostanie automatycznie pobrana.
AKTUALIZACJA: 16-10-2008 9:00 czasu pacyficznego:
To pytanie wywołało wiele świetnych odpowiedzi - dziękuję. Z odpowiedzi i moich badań wynika, że to, co naprawdę chcę zrobić, jest po prostu niemożliwe w Javie. Istnieją podejścia, takie jak mechanizm ServiceLoader firmy ddimitrov, który może działać - ale są one bardzo ciężkie dla tego, czego chcę, i uważam, że po prostu przenoszę problem z kodu Java do zewnętrznego pliku konfiguracyjnego. Aktualizacja 10.05.19 (11 lat później!) Istnieje obecnie kilka bibliotek, które mogą pomóc w tym według @ IvanNik za odpowiedź org.reflections wygląda dobrze. Również ClassGraph z @Luke Hutchison za odpowiedź wygląda interesująco. W odpowiedziach jest również kilka innych możliwości.
Innym sposobem określenia tego, czego chcę: funkcja statyczna w mojej klasie Animal wyszukuje i tworzy instancje wszystkich klas, które dziedziczą po Animal - bez dalszej konfiguracji / kodowania. Jeśli muszę skonfigurować, równie dobrze mogę po prostu utworzyć ich instancję w klasie Animal. Rozumiem to, ponieważ program Java to po prostu luźna federacja plików .class, tak właśnie jest.
Co ciekawe, wydaje się, że jest to dość trywialne w C #.
źródło
Odpowiedzi:
Używam org. Odbić :
Inny przykład:
źródło
animals.add( new c() );
twojego kodu), ponieważ chociażClass.newInstance()
istnieje, nie masz gwarancji, że dana klasa ma konstruktora bez parametrów (C # pozwala wymagać tego w ogólnym)Sposób w Javie, aby zrobić to, co chcesz, polega na użyciu mechanizmu ServiceLoader .
Również wiele osób tworzy własne, mając plik w dobrze znanej lokalizacji ścieżki klas (np. /META-INF/services/myplugin.properties), a następnie używając ClassLoader.getResources () do wyliczenia wszystkich plików o tej nazwie ze wszystkich plików jar. Dzięki temu każdy słoik może eksportować swoich własnych dostawców i możesz utworzyć ich wystąpienie przez odbicie za pomocą Class.forName ()
źródło
Pomyśl o tym z punktu widzenia aspektowego; tak naprawdę chcesz poznać wszystkie klasy w czasie wykonywania, które rozszerzyły klasę Animal. (Myślę, że to nieco dokładniejszy opis twojego problemu niż tytuł; w przeciwnym razie nie sądzę, że masz pytanie dotyczące czasu wykonania).
Więc myślę, że chcesz utworzyć konstruktor swojej klasy bazowej (Animal), który dodaje do twojej tablicy statycznej (ja wolę ArrayLists, siebie, ale do każdej z nich) typ bieżącej klasy, której instancja jest tworzona.
Więc z grubsza;
Oczywiście będziesz potrzebować statycznego konstruktora w Animal, aby zainicjować instancję instantiatedDerivedClass ... Myślę, że to zrobi to, czego prawdopodobnie chcesz. Zauważ, że jest to zależne od ścieżki wykonania; jeśli masz klasę Dog, która pochodzi od Animal, która nigdy nie jest wywoływana, nie będziesz jej mieć na swojej liście Animal Class.
źródło
Niestety nie jest to całkowicie możliwe, ponieważ ClassLoader nie powie Ci, jakie klasy są dostępne. Możesz jednak podejść dość blisko, robiąc coś takiego:
Edycja: johnstok ma rację (w komentarzach), że działa to tylko dla samodzielnych aplikacji Java i nie będzie działać na serwerze aplikacji.
źródło
URL[]
ale nie zawsze, więc może to nie być możliwe) z hierarchii ClassLoader. Często jednak musisz mieć pozwolenie, ponieważ zwykle maszSecurityManager
załadowany plik w JVM.Możesz użyć ResolverUtil ( surowe źródło ) z Stripes Framework,
jeśli potrzebujesz czegoś prostego i szybkiego bez refaktoryzacji istniejącego kodu.
Oto prosty przykład bez załadowania żadnej z klas:
Działa to również na serwerze aplikacji, ponieważ tam został zaprojektowany;)
Kod zasadniczo wykonuje następujące czynności:
ClassLoader#loadClass(String fullyQualifiedName)
Animal.class.isAssignableFrom(loadedClass);
źródło
Najbardziej niezawodnym mechanizmem wyświetlania wszystkich podklas danej klasy jest obecnie ClassGraph , ponieważ obsługuje on najszerszy możliwy zestaw mechanizmów specyfikacji ścieżek klas , w tym nowy system modułów JPMS. (Jestem autorem.)
źródło
Java dynamicznie ładuje klasy, więc Twój wszechświat klas obejmowałby tylko te, które zostały już załadowane (i jeszcze nie zostały usunięte). Być może możesz zrobić coś z niestandardowym programem ładującym klasy, który mógłby sprawdzić nadtypy każdej załadowanej klasy. Nie sądzę, aby istniał interfejs API do wysyłania zapytań do zestawu załadowanych klas.
źródło
Użyj tego
Edytować:
źródło
Dziękuję wszystkim, którzy odpowiedzieli na to pytanie.
Wygląda na to, że to naprawdę twardy orzech do zgryzienia. Skończyło się na tym, że poddałem się i utworzyłem statyczną tablicę i metodę pobierającą w mojej klasie bazowej.
Wygląda na to, że Java po prostu nie jest skonfigurowana do samodzielnego wykrywania, tak jak C #. Przypuszczam, że problem polega na tym, że ponieważ aplikacja Java jest po prostu zbiorem plików .class w jakimś katalogu / pliku jar, środowisko wykonawcze nie wie o klasie, dopóki nie zostanie przywołana. W tym czasie program ładujący go ładuje - próbuję go odkryć, zanim odwołam się do niego, co nie jest możliwe bez wejścia do systemu plików i sprawdzenia.
Zawsze lubię kod, który potrafi się odkryć, zamiast tego, żebym musiał o nim mówić, ale niestety to też działa.
Dzięki jeszcze raz!
źródło
Korzystając z OpenPojo , możesz wykonać następujące czynności:
źródło
Jest to trudny problem i będziesz musiał znaleźć te informacje za pomocą analizy statycznej, która nie jest łatwo dostępna w czasie wykonywania. Po prostu pobierz ścieżkę klas swojej aplikacji i przejrzyj dostępne klasy i przeczytaj informacje o kodzie bajtowym klasy, z której ona dziedziczy. Zwróć uwagę, że klasa Dog nie może bezpośrednio dziedziczyć po Animal, ale może dziedziczyć po Pet, który z kolei dziedziczy po Animal, więc będziesz musiał śledzić tę hierarchię.
źródło
Jednym ze sposobów jest użycie statycznych inicjatorów w klasach ... Nie sądzę, żeby były dziedziczone (nie zadziała, jeśli są):
Wymaga dodania tego kodu do wszystkich zaangażowanych klas. Ale unika gdzieś dużej, brzydkiej pętli, testując każdą klasę szukającą dzieci Animal.
źródło
Rozwiązałem ten problem dość elegancko, używając adnotacji na poziomie pakietu, a następnie ustawiając tę adnotację jako argument jako listę klas.
Znajdź klasy Java implementujące interfejs
Implementacje muszą po prostu utworzyć pakiet-info.java i umieścić magiczną adnotację wraz z listą klas, które chcą obsługiwać.
źródło