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.ServiceLoader
classe qui, lorsqu'elle reçoit un Class
objet, générera des instances de toutes les sous-classes déclarées de cette classe (ou, si Class
repré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 ServiceLoader
classe, et elle ne charge que les classes explicitement déclarées dans le META-INF/services
ré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/services
ré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 leurs Class
objets. 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.ExampleImpl
implémente cette interface:
package com.example;
public class ExampleImpl implements Example {
public String getStr() {
return "ExampleImpl's string.";
}
}
Vous déclareriez que la classe ExampleImpl
est une implémentation de Example
en créant un fichier META-INF/services/com.example.Example
contenant 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.