En fonction de vos besoins particuliers, dans certains cas, le mécanisme du chargeur de services Java peut atteindre ce que vous recherchez.
En bref, il permet aux développeurs de déclarer explicitement qu'une classe sous-classe une autre classe (ou implémente une interface) en la répertoriant dans un fichier dans le répertoire du fichier JAR / WAR META-INF/services. Il peut ensuite être découvert à l'aide de la java.util.ServiceLoaderclasse qui, lorsqu'elle reçoit un Classobjet, générera des instances de toutes les sous-classes déclarées de cette classe (ou, si Classreprésente une interface, toutes les classes implémentant cette interface).
Le principal avantage de cette approche est qu'il n'est pas nécessaire d'analyser manuellement le chemin de classe entier pour les sous-classes - toute la logique de découverte est contenue dans la ServiceLoaderclasse, et elle ne charge que les classes explicitement déclarées dans le META-INF/servicesrépertoire (pas toutes les classes sur le chemin de classe) .
Il existe cependant certains inconvénients:
- Il ne trouvera pas toutes les sous-classes, seulement celles qui sont explicitement déclarées. En tant que tel, si vous devez vraiment trouver toutes les sous-classes, cette approche peut être insuffisante.
- Il nécessite que le développeur déclare explicitement la classe sous le META-INF/servicesrépertoire. Ceci représente une charge supplémentaire pour le développeur et peut être sujet à erreurs.
- le ServiceLoader.iterator()génère des instances de sous-classe, pas leursClassobjets. Cela provoque deux problèmes:
- Vous n'avez pas votre mot à dire sur la façon dont les sous-classes sont construites - le constructeur no-arg est utilisé pour créer les instances.
- En tant que telles, les sous-classes doivent avoir un constructeur par défaut, ou doivent explicitement déclarer un constructeur sans argument.
 
Apparemment, Java 9 corrigera certaines de ces lacunes (en particulier celles concernant l'instanciation des sous-classes).
Un exemple
Supposons que vous souhaitiez trouver des classes qui implémentent une interface com.example.Example:
package com.example;
public interface Example {
    public String getStr();
}
La classe com.example.ExampleImplimplémente cette interface:
package com.example;
public class ExampleImpl implements Example {
    public String getStr() {
        return "ExampleImpl's string.";
    }
}
Vous déclareriez que la classe ExampleImplest une implémentation de Exampleen créant un fichier META-INF/services/com.example.Examplecontenant le texte com.example.ExampleImpl.
Ensuite, vous pouvez obtenir une instance de chaque implémentation de Example(y compris une instance de ExampleImpl) comme suit:
ServiceLoader<Example> loader = ServiceLoader.load(Example.class)
for (Example example : loader) {
    System.out.println(example.getStr());
}
// Prints "ExampleImpl's string.", plus whatever is returned
// by other declared implementations of com.example.Example.