Вопрос-ответ

How can I get a list of all the implementations of an interface programmatically in Java?

Как я могу получить список всех реализаций интерфейса программно в Java?

Могу ли я сделать это с помощью отражения или чего-то подобного?

Переведено автоматически
Ответ 1

Я некоторое время искал, и, похоже, существуют разные подходы, вот краткое изложение:


  1. библиотекаreflections довольно популярна, если вы не возражаете против добавления зависимости. Это будет выглядеть примерно так:


    Reflections reflections = new Reflections("firstdeveloper.examples.reflections");
    Set<Class<? extends Pet>> classes = reflections.getSubTypesOf(Pet.class);

  2. ServiceLoader (согласно ответу Эриксона), и это будет выглядеть примерно так:


    ServiceLoader<Pet> loader = ServiceLoader.load(Pet.class);
    for (Pet implClass : loader) {
    System.out.println(implClass.getClass().getSimpleName()); // prints Dog, Cat
    }

    Обратите внимание, что для этого вам нужно определить Petкак ServiceProviderInterface (SPI) и объявить его реализации. вы делаете это, создавая файл в resources/META-INF/services с именем examples.reflections.Pet и объявляя в нем все реализации Pet


    examples.reflections.Dog
    examples.reflections.Cat

  3. аннотация на уровне пакета. вот пример:


    Package[] packages = Package.getPackages();
    for (Package p : packages) {
    MyPackageAnnotation annotation = p.getAnnotation(MyPackageAnnotation.class);
    if (annotation != null) {
    Class<?>[] implementations = annotation.implementationsOfPet();
    for (Class<?> impl : implementations) {
    System.out.println(impl.getSimpleName());
    }
    }
    }

    и определение аннотации:


    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.PACKAGE)
    public @interface MyPackageAnnotation {
    Class<?>[] implementationsOfPet() default {};
    }

    и вы должны объявить аннотацию уровня пакета в файле с именем package-info.java внутри этого пакета. вот пример содержимого:


    @MyPackageAnnotation(implementationsOfPet = {Dog.class, Cat.class})
    package examples.reflections;

    Обратите внимание, что только пакеты, которые известны загрузчику классов на данный момент, будут загружены вызовом Package.getPackages().


Кроме того, существуют другие подходы, основанные на URLClassLoader, которые всегда будут ограничены классами, которые уже были загружены, если вы не выполняете поиск по каталогам.

Ответ 2

В общем, это дорого. Чтобы использовать отражение, класс должен быть загружен. Если вы хотите загрузить каждый класс, доступный в classpath, это потребует времени и памяти и не рекомендуется.

Если вы хотите избежать этого, вам нужно будет реализовать свой собственный анализатор файлов классов, который работал бы более эффективно, вместо reflection. Библиотека разработки байтового кода может помочь с этим подходом.

Механизм поставщика услуг является обычным средством перечисления реализаций подключаемого сервиса и стал более устоявшимся с введением Project Jigsaw (модулей) в Java 9. Используйте ServiceLoader в Java 6 или реализуйте свой собственный в более ранних версиях. Я привел пример в другом ответе.

Ответ 3

Что сказал Эриксон, но если вы все еще хотите это сделать, тогда взгляните на Reflections . С их страницы:


Используя отражения, вы можете запросить свои метаданные для:



  • получить все подтипы некоторого типа

  • получить все типы, аннотированные некоторой аннотацией

  • получить все типы, аннотированные некоторой аннотацией, включая соответствие параметров аннотации

  • получить все методы, аннотированные некоторыми


Ответ 4

У Spring есть довольно простой способ достичь этого:

public interface ITask {
void doStuff();
}

@Component
public class MyTask implements ITask {
public void doStuff(){}
}

Затем вы можете автоматически создать список типов ITask и Spring заполнит его всеми реализациями:

@Service
public class TaskService {

@Autowired
private List<ITask> tasks;
}
2023-05-16 06:15 java