Залежно від ваших конкретних вимог, в деяких випадках механізм завантажувача служб Java може досягти того, що вам потрібно.
Коротше кажучи, це дозволяє розробникам чітко заявляти, що клас підкласирує якийсь інший клас (або реалізує якийсь інтерфейс), перераховуючи його у файл у каталозі файлу JAR / WAR META-INF/services
. Потім його можна виявити за допомогою java.util.ServiceLoader
класу, який при наданні Class
об'єкта генерує екземпляри всіх оголошених підкласів цього класу (або, якщо Class
являє собою інтерфейс, усіх класів, що реалізують цей інтерфейс).
Основна перевага цього підходу полягає в тому, що немає необхідності вручну сканувати весь класний шлях на підкласи - вся логіка виявлення міститься в ServiceLoader
класі, і він завантажує лише класи, явно оголошені в META-INF/services
каталозі (не кожен клас на classpath) .
Однак є деякі недоліки:
- Він не знайде всіх підкласів, лише тих, які явно оголошені. Таким чином, якщо вам потрібно справді знайти всі підкласи, такий підхід може бути недостатнім.
- Він вимагає від розробника явно оголосити клас під
META-INF/services
каталогом. Це додаткове навантаження на розробника і може спричинити помилки.
ServiceLoader.iterator()
Створює екземпляри підкласу, а не їхня Class
об'єкти. Це викликає дві проблеми:
- Ви не можете сказати, як побудовані підкласи - конструктор no-arg використовується для створення екземплярів.
- Таким чином, підкласи повинні мати конструктор за замовчуванням або повинен експліцитно оголошувати конструктор без аргументів.
Мабуть, Java 9 вирішить деякі з цих недоліків (зокрема, ті, що стосуються інстанції підкласів).
Приклад
Припустимо, вам цікаво знайти класи, які реалізують інтерфейс com.example.Example
:
package com.example;
public interface Example {
public String getStr();
}
Клас com.example.ExampleImpl
реалізує цей інтерфейс:
package com.example;
public class ExampleImpl implements Example {
public String getStr() {
return "ExampleImpl's string.";
}
}
Ви б оголосили, що клас ExampleImpl
є реалізацією Example
, створивши файл, META-INF/services/com.example.Example
що містить текст com.example.ExampleImpl
.
Тоді ви можете отримати екземпляр кожної реалізації Example
(включаючи екземпляр ExampleImpl
) наступним чином:
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.