W czasie wykonywania Znajdź wszystkie klasy w aplikacji Java, które rozszerzają klasę bazową

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() );

Tak więc, chcę spojrzeć na wszystkie klasy w moim wszechświecie aplikacji, a kiedy znajdę taką, która pochodzi z Animal, chcę utworzyć nowy obiekt tego typu i dodać go do listy. Pozwala mi to na dodawanie funkcjonalności bez konieczności aktualizacji listy rzeczy. Mogę uniknąć:

List<Animal> animals = new ArrayList<Animal>();
animals.add( new Dog() );
animals.add( new Cat() );
animals.add( new Donkey() );
...

Z powyższym podejściem, mogę po prostu stworzyć nową klasę, która rozszerza Animal i zostanie odebrana automatycznie.

UPDATE:10/16/2008 9: 00 A. M. Pacific Standard Time:

To pytanie wywołało wiele świetnych odpowiedzi -- dziękuję. Z odpowiedzi i moich badań odkryłem, że to, co naprawdę chcę zrobić, nie jest możliwe w Javie. Istnieją podejścia, takie jak mechanizm ServiceLoader ddimitrova, które mogą działać -- ale są one bardzo ciężkie dla tego, co chcę, i wierzę, że po prostu przenieść problem z kodu Java do zewnętrznego pliku konfiguracyjnego.

Inny sposób na stwierdzenie, czego chcę: statyczna funkcja w mojej klasie Animal wyszukuje i tworzy instancje wszystkich klas, które dziedziczą po Animal - bez dalszej konfiguracji/kodowania. Jeśli będę musiał skonfigurować, równie dobrze mogę po prostu utworzyć ich instancję w klasie zwierząt. Rozumiem to, bo program Java to tylko luźna Federacja .pliki klas, które tak właśnie jest.

Co ciekawe, wydaje się, że jest to dość trywialne W C#.

Author: sschuberth, 2008-10-15

13 answers

Używam org.refleksje :

Reflections reflections = new Reflections("com.mycompany");    
Set<Class<? extends MyInterface>> classes = reflections.getSubTypesOf(MyInterface.class);
 131
Author: IvanNik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-23 14:35:15

Sposobem Javy, aby zrobić to, co chcesz, jest użycie mechanizmu ServiceLoader .

Również wiele osób robi swoje, mając plik w znanej lokalizacji classpath (np. / META-INF/services / myplugin.właściwości), a następnie używając ClassLoader.getResources () aby wyliczyć wszystkie pliki o tej nazwie ze wszystkich słoików. Pozwala to każdemu jar eksportować własnych dostawców i można utworzyć ich instancję przez odbicie za pomocą klasy.forName ()

 32
Author: ddimitrov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-08 23:44:56

Pomyśl o tym z punktu widzenia aspektu; to, co chcesz zrobić, naprawdę, to znać wszystkie klasy w czasie wykonywania, które rozszerzyły klasę zwierząt. (Myślę, że jest to nieco dokładniejszy opis Twojego problemu niż tytuł; w przeciwnym razie, nie sądzę, że masz pytanie runtime.)

Więc myślę, że chcesz stworzyć konstruktor swojej klasy bazowej (Animal), który dodaje do tablicy statycznej (wolę ArrayLists, sam, ale do każdej z nich) Typ bieżąca Klasa, która jest tworzona.

Więc z grubsza;

public abstract class Animal
    {
    private static ArrayList<Class> instantiatedDerivedTypes;
    public Animal() {
        Class derivedClass = this.getClass();
        if (!instantiatedDerivedClass.contains(derivedClass)) {
            instantiatedDerivedClass.Add(derivedClass);
        }
    }

Oczywiście, będziesz potrzebował statycznego konstruktora na Animalu, aby zainicjować instantiatedDerivedClass... Myślę, że to zrobi to, czego prawdopodobnie chcesz. Zauważ, że jest to zależne od ścieżki wykonania; jeśli masz klasę psa, która wywodzi się od zwierzęcia, które nigdy nie zostanie wywołane, nie będziesz mieć jej na liście klas zwierząt.

 8
Author: Paul Sonier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-15 21:07:09

Niestety nie jest to całkowicie możliwe, ponieważ ClassLoader nie powie Ci, jakie klasy są dostępne. Można jednak podejść dość blisko robiąc coś takiego:

for (String classpathEntry : System.getProperty("java.class.path").split(System.getProperty("path.separator"))) {
    if (classpathEntry.endsWith(".jar")) {
        File jar = new File(classpathEntry);

        JarInputStream is = new JarInputStream(new FileInputStream(jar));

        JarEntry entry;
        while( (entry = is.getNextJarEntry()) != null) {
            if(entry.getName().endsWith(".class")) {
                // Class.forName(entry.getName()) and check
                //   for implementation of the interface
            }
        }
    }
}

Edit: johnstok ma rację (w komentarzach), że to działa tylko dla samodzielnych aplikacji Java i nie będzie działać pod serwerem aplikacji.

 6
Author: jonathan-stafford,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-21 22:02:27

Możesz użyć ResolverUtil (raw source ) z frameworka
jeśli potrzebujesz czegoś prostego i szybkiego bez refaktoryzacji istniejącego kodu.

Oto prosty przykład bez załadowania żadnej z klas:

package test;

import java.util.Set;
import net.sourceforge.stripes.util.ResolverUtil;

public class BaseClassTest {
    public static void main(String[] args) throws Exception {
        ResolverUtil<Animal> resolver = new ResolverUtil<Animal>();
        resolver.findImplementations(Animal.class, "test");
        Set<Class<? extends Animal>> classes = resolver.getClasses();

        for (Class<? extends Animal> clazz : classes) {
            System.out.println(clazz);
        }
    }
}

class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
class Donkey extends Animal {}
To działa również na serwerze aplikacji, ponieważ właśnie tam został zaprojektowany do pracy;)]}

Kod zasadniczo wykonuje następujące czynności:

  • iteracja wszystkich zasobów w pakiecie) określasz
  • Zachowaj tylko zasoby kończące się.Klasa
  • załaduj te klasy używając ClassLoader#loadClass(String fullyQualifiedName)
  • sprawdza, czy Animal.class.isAssignableFrom(loadedClass);
 4
Author: DJDaveMark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-21 12:07:50

Java dynamicznie ładuje klasy, więc twój wszechświat klas będzie tylko tymi, które zostały już załadowane (i nie zostały jeszcze rozładowane). Być może możesz zrobić coś z niestandardowym ładowaniem klas, które może sprawdzić supertypy każdej załadowanej klasy. Myślę, że nie ma API do odpytywania zestawu załadowanych klas.

 2
Author: thoroughly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-15 17:55:20

Dziękuję wszystkim, którzy odpowiedzieli na to pytanie.

Wygląda na to, że to naprawdę trudny orzech do zgryzienia. Ostatecznie poddałem się i stworzyłem statyczną tablicę i getter w mojej baseclass.
public abstract class Animal{
    private static Animal[] animals= null;
    public static Animal[] getAnimals(){
        if (animals==null){
            animals = new Animal[]{
                new Dog(),
                new Cat(),
                new Lion()
            };
        }
        return animals;
    }
}

Wydaje się, że Java po prostu nie jest skonfigurowana do samodzielnego odkrywania, tak jak C#. Przypuszczam, że problem polega na tym, że ponieważ aplikacja Java jest tylko zbiorem .pliki klas znajdujące się gdzieś w katalogu / pliku jar, środowisko wykonawcze nie wie o klasie, dopóki nie zostanie do niej odwołane. W tym czasie ładowarka Ładuje próbuję go odkryć, zanim się do niego odwołam, co nie jest możliwe bez wyjścia do systemu plików i szukania.

Zawsze lubię kod, który może się odkryć, zamiast mówić o sobie, ale niestety to też działa.

Jeszcze raz dziękuję!

 1
Author: JohnnyLambada,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-15 19:04:01

Używając OpenPojo możesz wykonać następujące czynności:

String package = "com.mycompany";
List<Animal> animals = new ArrayList<Animal>();

for(PojoClass pojoClass : PojoClassFactory.enumerateClassesByExtendingType(package, Animal.class, null) {
  animals.add((Animal) InstanceFactory.getInstance(pojoClass));
}
 1
Author: Osman Shoukry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-10 05:23:24

Użyj tego

public static Set<Class> getExtendedClasses(Class superClass)
{
    try
    {
        ResolverUtil resolver = new ResolverUtil();
        resolver.findImplementations(superClass, superClass.getPackage().getName());
        return resolver.getClasses();  
    }
    catch(Exception e)
    {Log.d("Log:", " Err: getExtendedClasses() ");}

    return null;
}

getExtendedClasses(Animals.class);

Edit:

  • biblioteka dla (ResolverUtil):
 1
Author: Ali Bagheri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-03 06:15:42

Spróbuj ClassGraph . (Zastrzeżenie, jestem autorem). Dla podanego przykładu, Kod ClassGraph będzie:

List<Animal> animals =
    new ClassGraph()
        .whitelistPackages("com.zoo.animals")
        .enableAllInfo()
        .scan()
        .getSubclasses(Animal.class.getName())
        .loadClasses(Animal.class);

ClassGraph może zrobić o wiele więcej niż to -- Sprawdź API .

 1
Author: Luke Hutchison,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-01 03:51:24

Jest to trudny problem i musisz dowiedzieć się tych informacji za pomocą analizy statycznej, jej nie jest łatwo dostępne w czasie wykonywania. Zasadniczo Pobierz classpath swojej aplikacji i przeskanuj dostępne klasy i odczytaj informacje o kodzie bajtowym klasy, od której dziedziczy. Zauważ, że pies klasowy może nie dziedziczyć bezpośrednio od zwierzęcia, ale może dziedziczyć od zwierzęcia, które jest dziedziczeniem turn od zwierzęcia,więc będziesz musiał śledzić tę hierarchię.

 0
Author: pdeva,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-15 18:52:00

Jednym ze sposobów jest zmuszenie klas do użycia statycznych inicjalizatorów... Nie sądzę, że są dziedziczone (nie zadziała, jeśli są):

public class Dog extends Animal{

static
{
   Animal a = new Dog();
   //add a to the List
}

Wymaga dodania tego kodu do wszystkich klas. Ale unika gdzieś Wielkiej brzydkiej pętli, testując każdą klasę w poszukiwaniu dzieci zwierząt.

 0
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-15 19:29:37

Rozwiązałem ten problem dość elegancko używając adnotacji na poziomie pakietu, a następnie sprawiając, że adnotacja ma jako argument listę klas.

Znajdź klasy Java implementujące interfejs

Implementacje muszą tylko utworzyć pakiet-info.java i umieść magiczną adnotację z listą klas, które chcą obsługiwać.

 0
Author: Adam Gent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:24