Jak się porusza i próbuje znaleźć wszystkie podklasy danej klasy (lub wszystkich implementatorów danego interfejsu) w Javie? Na razie mam metodę, aby to zrobić, ale uważam, że jest to dość nieefektywne (co najmniej). Metoda jest następująca:
- Uzyskaj listę wszystkich nazw klas, które istnieją na ścieżce klasy
- Załaduj każdą klasę i sprawdź, czy jest to podklasa lub implementator pożądanej klasy lub interfejsu
W Eclipse istnieje przyjemna funkcja zwana Hierarchią Typów, która udowadnia to dość skutecznie. Jak się porusza i robi to programowo?
Odpowiedzi:
Nie ma innego sposobu niż to, co opisałeś. Pomyśl o tym - skąd ktokolwiek może wiedzieć, jakie klasy rozszerzają ClassX bez skanowania każdej klasy na ścieżce klas?
Zaćmienie może powiedzieć ci tylko o superklasach i podklasach w czasie, który wydaje się być „efektywny”, ponieważ ma już załadowane wszystkie dane typu w punkcie, w którym naciskasz przycisk „Wyświetlaj w hierarchii typów” (ponieważ jest to ciągłe kompilowanie twoich zajęć, wie o wszystkim na ścieżce klas itp.).
źródło
reflections.getSubTypesOf(aClazz))
linkSkanowanie w poszukiwaniu klas nie jest łatwe dzięki czystej Javie.
Struktura Spring oferuje klasę o nazwie ClassPathScanningCandidateComponentProvider, która może zrobić to, czego potrzebujesz. Poniższy przykład znalazłby wszystkie podklasy MyClass w pakiecie org.example.package
Dodatkową zaletą tej metody jest użycie analizatora kodu bajtowego do znalezienia kandydatów, co oznacza, że nie załaduje wszystkich skanowanych klas.
źródło
Nie jest to możliwe przy użyciu tylko wbudowanego interfejsu API Java Reflections.
Istnieje projekt, który wykonuje niezbędne skanowanie i indeksowanie ścieżki klasy, aby uzyskać dostęp do tych informacji ...
Refleksje
(zrzeczenie się: Nie korzystałem z niego, ale opis projektu wydaje się dokładnie pasować do twoich potrzeb).
źródło
Nie zapominaj, że wygenerowany Javadoc dla klasy będzie zawierał listę znanych podklas (a dla interfejsów - znanych klas implementujących).
źródło
Wypróbuj ClassGraph . (Zastrzeżenie, jestem autorem). ClassGraph obsługuje skanowanie podklas danej klasy, w czasie wykonywania lub w czasie kompilacji, ale także o wiele więcej. ClassGraph może zbudować abstrakcyjną reprezentację całego wykresu klas (wszystkich klas, adnotacji, metod, parametrów metod i pól) w pamięci, dla wszystkich klas na ścieżce klas lub dla klas w pakietach umieszczonych na białej liście, i możesz jednak zapytać o ten wykres klas chcesz. ClassGraph obsługuje więcej mechanizmów specyfikacji ścieżek klas i programów ładujących klasy niż jakikolwiek inny skaner, a także bezproblemowo współpracuje z nowym systemem modułów JPMS, więc jeśli oprzesz swój kod na ClassGraph, Twój kod będzie maksymalnie przenośny. Zobacz API tutaj.
źródło
Zrobiłem to kilka lat temu. Najbardziej niezawodnym sposobem na to (tj. Z oficjalnymi interfejsami API Java i bez zewnętrznych zależności) jest napisanie niestandardowego dokumentu, aby utworzyć listę, którą można odczytać w czasie wykonywania.
Możesz uruchomić go z wiersza poleceń w następujący sposób:
lub uruchom go z ant w ten sposób:
Oto podstawowy kod:
Dla uproszczenia usunąłem analizę argumentów wiersza poleceń i piszę do System.out zamiast pliku.
źródło
Wiem, że spóźniłem się na tę imprezę kilka lat, ale natknąłem się na to pytanie, próbując rozwiązać ten sam problem. Możesz użyć wewnętrznego wyszukiwania Eclipse programowo, jeśli piszesz wtyczkę Eclipse (i dzięki temu korzystasz z ich buforowania itp.), Aby znaleźć klasy, które implementują interfejs. Oto moje (bardzo szorstkie) pierwsze cięcie:
Pierwszym problemem, jaki do tej pory widzę, jest to, że łapię tylko klasy bezpośrednio implementujące interfejs, a nie wszystkie ich podklasy - ale niewielka rekurencja nikomu nie zaszkodzi.
źródło
Pamiętając o ograniczeniach wymienionych w innych odpowiedziach, możesz również użyć openpojo
PojoClassFactory
( dostępne w Maven ) w następujący sposób:Gdzie
packageRoot
jest główny ciąg pakietów, w których chcesz wyszukiwać (np. A"com.mycompany"
nawet tylko"com"
), a takżeSuperclass
twój nadtyp (działa to również na interfejsach).źródło
Właśnie piszę proste demo, aby użyć
org.reflections.Reflections
podklas klasy abstrakcyjnej:https://github.com/xmeng1/ReflectionsDemo
źródło
W zależności od konkretnych wymagań w niektórych przypadkach mechanizm modułu ładującego usługi Java może osiągnąć to, czego szukasz.
Krótko mówiąc, pozwala programistom jawnie zadeklarować, że klasa podklasuje jakąś inną klasę (lub implementuje jakiś interfejs), umieszczając ją w pliku w katalogu pliku JAR / WAR
META-INF/services
. Można go następnie odkryć za pomocąjava.util.ServiceLoader
klasy, która po otrzymaniuClass
obiektu wygeneruje instancje wszystkich zadeklarowanych podklas tej klasy (lub, jeśliClass
reprezentuje interfejs, wszystkie klasy implementujące ten interfejs).Główną zaletą tego podejścia jest to, że nie ma potrzeby ręcznego skanowania całej ścieżki klasy w poszukiwaniu podklas - cała logika wykrywania jest zawarta w
ServiceLoader
klasie i ładuje tylko klasy jawnie zadeklarowane wMETA-INF/services
katalogu (nie każda klasa w ścieżce klasy) .Istnieją jednak pewne wady:
META-INF/services
katalogu. Jest to dodatkowe obciążenie dla programisty i może być podatne na błędy.ServiceLoader.iterator()
Generuje podklasy przykłady, a nie ichClass
obiektami. Powoduje to dwa problemy:Najwyraźniej Java 9 rozwiąże niektóre z tych niedociągnięć (w szczególności te dotyczące tworzenia podklas).
Przykład
Załóżmy, że chcesz znaleźć klasy, które implementują interfejs
com.example.Example
:Klasa
com.example.ExampleImpl
implementuje ten interfejs:Zadeklarowałbyś, że klasa
ExampleImpl
jest implementacjąExample
poprzez utworzenie plikuMETA-INF/services/com.example.Example
zawierającego tekstcom.example.ExampleImpl
.Następnie można uzyskać instancję każdej implementacji
Example
(w tym instancjęExampleImpl
) w następujący sposób:źródło
Należy również zauważyć, że to oczywiście znajdzie tylko te wszystkie podklasy, które istnieją w bieżącej ścieżce klasy. Przypuszczalnie jest to OK dla tego, na co obecnie patrzysz, i są szanse, że wziąłeś to pod uwagę, ale jeśli w dowolnym momencie wypuściłeś nieklasyczną
final
dziką przyrodę (dla różnych poziomów „dzikiej”), to jest całkowicie możliwe, że ktoś inny napisał własną podklasę, o której nie będziesz wiedzieć.Jeśli więc chciałeś zobaczyć wszystkie podklasy, ponieważ chcesz wprowadzić zmiany i zobaczysz, jak wpływa to na zachowanie podklas - pamiętaj o tych podklasach, których nie widzisz. Idealnie wszystkie twoje nieprywatne metody, a sama klasa powinna być dobrze udokumentowana; wprowadź zmiany zgodnie z tą dokumentacją bez zmiany semantyki metod / pól nieprywatnych, a twoje zmiany powinny być kompatybilne wstecz, dla każdej podklasy, która przynajmniej była zgodna z twoją definicją nadklasy.
źródło
Różnica między implementacją a środowiskiem Eclipse jest widoczna, ponieważ skanowanie odbywa się za każdym razem, podczas gdy środowisko Eclipse (i inne narzędzia) skanuje tylko raz (podczas ładowania projektu przez większość czasu) i tworzy indeks. Następnym razem, gdy poprosisz o dane, dane nie będą skanowane ponownie, ale spójrz na indeks.
źródło
Używam biblioteki refleksyjnej, która skanuje ścieżkę klasy dla wszystkich podklas: https://github.com/ronmamo/reflections
Tak by to było zrobić:
źródło
Dodaj je do mapy statycznej wewnątrz (this.getClass (). GetName ()) konstruktora klas nadrzędnych (lub utwórz domyślną), ale zostanie to zaktualizowane w czasie wykonywania. Jeśli leniwa inicjalizacja jest opcją, możesz wypróbować to podejście.
źródło
Możesz użyć biblioteki org.reflections, a następnie utworzyć obiekt klasy Reflections. Za pomocą tego obiektu można uzyskać listę wszystkich podklas danej klasy. https://www.javadoc.io/doc/org.reflections/reflections/0.9.10/org/reflections/Reflections.html
źródło
Musiałem to zrobić jako przypadek testowy, aby sprawdzić, czy do kodu dodano nowe klasy. Oto co zrobiłem
źródło