Je veux faire quelque chose comme ça:
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() );
Donc, je veux regarder toutes les classes de l'univers de mon application, et quand j'en trouve une qui descend d'Animal, je veux créer un nouvel objet de ce type et l'ajouter à la liste. Cela me permet d'ajouter des fonctionnalités sans avoir à mettre à jour une liste de choses. Je peux éviter ce qui suit:
List<Animal> animals = new ArrayList<Animal>();
animals.add( new Dog() );
animals.add( new Cat() );
animals.add( new Donkey() );
...
Avec l'approche ci-dessus, je peux simplement créer une nouvelle classe qui étend Animal et elle sera automatiquement récupérée.
MISE À JOUR: 16/10/2008 9h00, heure normale du Pacifique:
Cette question a généré beaucoup de bonnes réponses - merci. D'après les réponses et mes recherches, j'ai trouvé que ce que je veux vraiment faire n'est tout simplement pas possible sous Java. Il existe des approches, telles que le mécanisme ServiceLoader de ddimitrov qui peuvent fonctionner - mais elles sont très lourdes pour ce que je veux, et je crois que je déplace simplement le problème du code Java vers un fichier de configuration externe. Mise à jour 10/05/19 (11 ans plus tard!) Il existe maintenant plusieurs bibliothèques qui peuvent vous aider, selon la réponse de @ IvanNik org.reflections semble bonne. Aussi ClassGraph de @Luke de Hutchison réponse semble intéressante. Il y a aussi plusieurs autres possibilités dans les réponses.
Une autre façon de dire ce que je veux: une fonction statique dans ma classe Animal trouve et instancie toutes les classes qui héritent d'Animal - sans autre configuration / codage. Si je dois configurer, je pourrais tout aussi bien les instancier dans la classe Animal de toute façon. Je comprends cela parce qu'un programme Java est juste une fédération lâche de fichiers .class que c'est comme ça.
Fait intéressant, il semble que ce soit assez trivial en C #.
la source
Réponses:
J'utilise org.reflections :
Un autre exemple:
la source
animals.add( new c() );
de votre code), car tant qu'ilClass.newInstance()
existe, vous n'avez aucune garantie que la classe spécifique a un constructeur sans paramètre (C # vous permet de l'exiger dans un générique)La manière Java de faire ce que vous voulez est d'utiliser le mécanisme ServiceLoader .
De plus, beaucoup de gens utilisent le leur en ayant un fichier dans un emplacement de chemin de classe bien connu (c'est-à-dire /META-INF/services/myplugin.properties), puis en utilisant ClassLoader.getResources () pour énumérer tous les fichiers avec ce nom de tous les jars. Cela permet à chaque jar d'exporter ses propres fournisseurs et vous pouvez les instancier par réflexion à l'aide de Class.forName ()
la source
Pensez-y d'un point de vue orienté aspect; ce que vous voulez faire, vraiment, c'est connaître toutes les classes au moment de l'exécution qui ont étendu la classe Animal. (Je pense que c'est une description légèrement plus précise de votre problème que votre titre; sinon, je ne pense pas que vous ayez une question d'exécution.)
Donc, ce que je pense que vous voulez, c'est créer un constructeur de votre classe de base (Animal) qui ajoute à votre tableau statique (je préfère ArrayLists, moi-même, mais à chacun le leur) le type de la classe actuelle qui est instanciée.
Donc, à peu près;
Bien sûr, vous aurez besoin d'un constructeur statique sur Animal pour initialiser instanciéDerivedClass ... Je pense que cela fera ce que vous voulez probablement. Notez que cela dépend du chemin d'exécution; si vous avez une classe Dog qui dérive d'Animal qui n'est jamais invoquée, vous ne l'aurez pas dans votre liste de classes d'animaux.
la source
Malheureusement, ce n'est pas tout à fait possible car le ClassLoader ne vous dira pas quelles classes sont disponibles. Vous pouvez cependant vous en rapprocher assez en faisant quelque chose comme ceci:
Edit: johnstok a raison (dans les commentaires) que cela ne fonctionne que pour les applications Java autonomes et ne fonctionnera pas sous un serveur d'applications.
la source
URL[]
mais pas toujours, donc cela peut ne pas être possible) à partir de la hiérarchie ClassLoader. Souvent, cependant, vous devez avoir une autorisation car généralement vous avez unSecurityManager
fichier chargé dans la JVM.Vous pouvez utiliser ResolverUtil ( source brute ) de Stripes Framework
si vous avez besoin de quelque chose de simple et rapide sans refactoriser le code existant.
Voici un exemple simple n'ayant chargé aucune des classes:
Cela fonctionne également dans un serveur d'applications car c'est là qu'il a été conçu pour fonctionner;)
Le code fait essentiellement ce qui suit:
ClassLoader#loadClass(String fullyQualifiedName)
Animal.class.isAssignableFrom(loadedClass);
la source
Le mécanisme le plus robuste pour lister toutes les sous-classes d'une classe donnée est actuellement ClassGraph , car il gère le plus large éventail possible de mécanismes de spécification de chemin de classe , y compris le nouveau système de modules JPMS. (Je suis l'auteur.)
la source
Java charge dynamiquement les classes, de sorte que votre univers de classes ne serait que celles qui ont déjà été chargées (et pas encore déchargées). Vous pouvez peut-être faire quelque chose avec un chargeur de classe personnalisé qui pourrait vérifier les supertypes de chaque classe chargée. Je ne pense pas qu'il existe une API pour interroger l'ensemble des classes chargées.
la source
utilisez ceci
Éditer:
la source
Merci à tous ceux qui ont répondu à cette question.
Il semble que ce soit en effet une noix difficile à casser. J'ai fini par abandonner et créer un tableau statique et un getter dans ma classe de base.
Il semble que Java n'est tout simplement pas configuré pour l'auto-découverte comme l'est C #. Je suppose que le problème est que, comme une application Java n'est qu'une collection de fichiers .class dans un répertoire / fichier jar quelque part, le moteur d'exécution ne connaît pas une classe tant qu'elle n'est pas référencée. À ce moment-là, le chargeur le charge - ce que j'essaie de faire est de le découvrir avant de le référencer, ce qui n'est pas possible sans aller dans le système de fichiers et regarder.
J'aime toujours le code qui peut se découvrir au lieu d'avoir à le dire sur lui-même, mais hélas cela fonctionne aussi.
Merci encore!
la source
En utilisant OpenPojo, vous pouvez effectuer les opérations suivantes:
la source
Il s'agit d'un problème difficile et vous devrez trouver ces informations à l'aide de l'analyse statique, qui n'est pas facilement disponible au moment de l'exécution. En gros, récupérez le chemin de classe de votre application, parcourez les classes disponibles et lisez les informations de bytecode d'une classe dont elle hérite. Notez qu'une classe Dog ne peut pas hériter directement d'Animal mais peut hériter de Pet qui hérite à son tour d'Animal, vous devrez donc garder une trace de cette hiérarchie.
la source
Une façon est de faire en sorte que les classes utilisent des initialiseurs statiques ... Je ne pense pas que ceux-ci soient hérités (cela ne fonctionnera pas s'ils le sont):
Il vous oblige à ajouter ce code à toutes les classes impliquées. Mais cela évite d'avoir une grosse boucle moche quelque part, testant chaque classe à la recherche d'enfants d'Animal.
la source
J'ai résolu ce problème assez élégamment en utilisant les annotations au niveau du package, puis en faisant en sorte que cette annotation ait comme argument une liste de classes.
Rechercher des classes Java implémentant une interface
Les implémentations doivent simplement créer un package-info.java et y mettre l'annotation magique avec la liste des classes qu'elles veulent prendre en charge.
la source