A seconda delle tue esigenze particolari, in alcuni casi il meccanismo del caricatore di servizi Java potrebbe raggiungere ciò che cerchi.
In breve, consente agli sviluppatori di dichiarare esplicitamente che una classe subclasse qualche altra classe (o implementa qualche interfaccia) elencandola in un file nella directory del file JAR / WAR META-INF/services
. Può quindi essere scoperto usando la java.util.ServiceLoader
classe che, quando viene dato un Class
oggetto, genererà istanze di tutte le sottoclassi dichiarate di quella classe (o, se ilClass
rappresenta un'interfaccia, tutte le classi che implementano quell'interfaccia).
Il vantaggio principale di questo approccio è che non è necessario scansionare manualmente l'intero percorso di classe per sottoclassi: tutta la logica di rilevamento è contenuta nella ServiceLoader
classe e carica solo le classi dichiarate esplicitamente nella META-INF/services
directory (non tutte le classi sul percorso di classe) .
Vi sono, tuttavia, alcuni svantaggi:
- Non troverà tutte le sottoclassi, solo quelle dichiarate esplicitamente. Pertanto, se è necessario trovare davvero tutte le sottoclassi, questo approccio potrebbe non essere sufficiente.
- Richiede allo sviluppatore di dichiarare esplicitamente la classe nella
META-INF/services
directory. Questo è un onere aggiuntivo per lo sviluppatore e può essere soggetto a errori.
- Il
ServiceLoader.iterator()
genera istanze della sottoclasse, non la loroClass
oggetti. Ciò causa due problemi:
- Non si può dire nulla su come sono costruite le sottoclassi: il costruttore no-arg viene utilizzato per creare le istanze.
- Come tale, le sottoclassi devono avere un costruttore predefinito o esplicitamente dichiarare un costruttore no-arg.
Apparentemente Java 9 affronterà alcune di queste carenze (in particolare quelle relative all'istanza delle sottoclassi).
Un esempio
Supponiamo che tu sia interessato a trovare classi che implementano un'interfaccia com.example.Example
:
package com.example;
public interface Example {
public String getStr();
}
La classe com.example.ExampleImpl
implementa tale interfaccia:
package com.example;
public class ExampleImpl implements Example {
public String getStr() {
return "ExampleImpl's string.";
}
}
Dichiareresti che la classe ExampleImpl
è un'implementazione di Example
creando un file META-INF/services/com.example.Example
contenente il testo com.example.ExampleImpl
.
Quindi, è possibile ottenere un'istanza di ogni implementazione di Example
(inclusa un'istanza di ExampleImpl
) come segue:
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.