Как вы находите все подклассы данного класса в Java? - PullRequest
190 голосов
/ 29 января 2009

Как можно найти и попытаться найти все подклассы данного класса (или всех разработчиков данного интерфейса) в Java? На данный момент у меня есть способ сделать это, но я нахожу его довольно неэффективным (если не сказать больше). Метод:

  1. Получить список всех имен классов, существующих в пути к классам
  2. Загрузить каждый класс и проверить, является ли он подклассом или разработчиком нужного класса или интерфейса

В Eclipse есть замечательная функция, которая называется Type Hierarchy, которая позволяет показать это довольно эффективно. Как это сделать и сделать это программно?

Ответы [ 15 ]

119 голосов
/ 30 января 2009

Сканирование для классов не просто с чистой Java.

Spring Framework предлагает класс ClassPathScanningCandidateComponentProvider , который может делать то, что вам нужно. В следующем примере все подклассы MyClass будут найдены в пакете org.example.package

ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.addIncludeFilter(new AssignableTypeFilter(MyClass.class));

// scan in org.example.package
Set<BeanDefinition> components = provider.findCandidateComponents("org/example/package");
for (BeanDefinition component : components)
{
    Class cls = Class.forName(component.getBeanClassName());
    // use class cls found
}

Этот метод имеет дополнительное преимущество использования анализатора байт-кода для поиска кандидатов, что означает, что он будет не загружать все сканируемые классы.

69 голосов
/ 29 января 2009

Нет другого способа сделать это, кроме того, что вы описали. Подумайте об этом - как кто-нибудь может узнать, какие классы расширяют ClassX, не сканируя каждый класс на пути к классам?

Eclipse может рассказать вам о супер и подклассах только в то, что кажется «эффективным» промежутком времени, поскольку в него уже загружены все типовые данные в точке, где вы нажимаете кнопку «Показать в иерархии типов» поскольку он постоянно компилирует ваши классы, знает обо всем на пути к классам и т. д.).

46 голосов
/ 29 января 2009

Это невозможно сделать, используя только встроенный Java Reflections API.

Существует проект, который выполняет необходимое сканирование и индексацию вашего пути к классам, чтобы вы могли получить доступ к этой информации ...

Отражения

Анализ метаданных среды выполнения Java в духе Scannotations

Reflections сканирует ваш путь к классам, индексирует метаданные, позволяет запрашивать их во время выполнения и может сохранять и собирать эту информацию для многих модулей в вашем проекте.

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

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

(отказ от ответственности: я не использовал его, но описание проекта, похоже, точно соответствует вашим потребностям.)

10 голосов
/ 30 января 2009

Не забывайте, что сгенерированный Javadoc для класса будет включать список известных подклассов (и для интерфейсов, известных реализующих классов).

8 голосов
/ 15 мая 2012

Я сделал это несколько лет назад. Самый надежный способ сделать это (то есть с официальными API-интерфейсами Java и без внешних зависимостей) - это написать собственный доклет для создания списка, который можно прочитать во время выполнения.

Вы можете запустить его из командной строки следующим образом:

javadoc -d build -doclet com.example.ObjectListDoclet -sourcepath java/src -subpackages com.example

или запустите его из муравья следующим образом:

<javadoc sourcepath="${src}" packagenames="*" >
  <doclet name="com.example.ObjectListDoclet" path="${build}"/>
</javadoc>

Вот основной код:

public final class ObjectListDoclet {
    public static final String TOP_CLASS_NAME =  "com.example.MyClass";        

    /** Doclet entry point. */
    public static boolean start(RootDoc root) throws Exception {
        try {
            ClassDoc topClassDoc = root.classNamed(TOP_CLASS_NAME);
            for (ClassDoc classDoc : root.classes()) {
                if (classDoc.subclassOf(topClassDoc)) {
                    System.out.println(classDoc);
                }
            }
            return true;
        }
        catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }
}

Для простоты я удалил разбор аргументов командной строки и пишу в System.out, а не в файл.

7 голосов
/ 11 августа 2013

Принимая во внимание ограничения, упомянутые в других ответах, вы также можете использовать openpojo's PojoClassFactory ( доступно в Maven ) следующим образом:

for(PojoClass pojoClass : PojoClassFactory.enumerateClassesByExtendingType(packageRoot, Superclass.class, null)) {
    System.out.println(pojoClass.getClazz());
}

Где packageRoot - корневая строка пакетов, в которых вы хотите искать (например, "com.mycompany" или даже просто "com"), а Superclass - ваш супертип (это работает и на интерфейсах).

7 голосов
/ 20 апреля 2011

Я знаю, что опоздал на эту вечеринку на несколько лет, но я наткнулся на этот вопрос, пытаясь решить ту же проблему. Вы можете использовать внутренний поиск Eclipse программно, если вы пишете плагин Eclipse (и, таким образом, используете их кэширование и т. Д.), Чтобы найти классы, которые реализуют интерфейс. Вот мой (очень грубый) первый разрез:

  protected void listImplementingClasses( String iface ) throws CoreException
  {
    final IJavaProject project = <get your project here>;
    try
    {
      final IType ifaceType = project.findType( iface );
      final SearchPattern ifacePattern = SearchPattern.createPattern( ifaceType, IJavaSearchConstants.IMPLEMENTORS );
      final IJavaSearchScope scope = SearchEngine.createWorkspaceScope();
      final SearchEngine searchEngine = new SearchEngine();
      final LinkedList<SearchMatch> results = new LinkedList<SearchMatch>();
      searchEngine.search( ifacePattern, 
      new SearchParticipant[]{ SearchEngine.getDefaultSearchParticipant() }, scope, new SearchRequestor() {

        @Override
        public void acceptSearchMatch( SearchMatch match ) throws CoreException
        {
          results.add( match );
        }

      }, new IProgressMonitor() {

        @Override
        public void beginTask( String name, int totalWork )
        {
        }

        @Override
        public void done()
        {
          System.out.println( results );
        }

        @Override
        public void internalWorked( double work )
        {
        }

        @Override
        public boolean isCanceled()
        {
          return false;
        }

        @Override
        public void setCanceled( boolean value )
        {
        }

        @Override
        public void setTaskName( String name )
        {
        }

        @Override
        public void subTask( String name )
        {
        }

        @Override
        public void worked( int work )
        {
        }

      });

    } catch( JavaModelException e )
    {
      e.printStackTrace();
    }
  }

Первая проблема, которую я вижу до сих пор, заключается в том, что я ловлю только те классы, которые непосредственно реализуют интерфейс, а не все их подклассы - но небольшая рекурсия никогда никому не повредит.

6 голосов
/ 24 сентября 2015

Попробуйте ClassGraph . (Отказ от ответственности, я автор). ClassGraph поддерживает сканирование для подклассов данного класса, во время выполнения или во время сборки, но также и многое другое ClassGraph может построить абстрактное представление всего графа классов (все классы, аннотации, методы, параметры методов и поля) в памяти, для всех классов в пути к классам или для классов в пакетах из белого списка, и вы можете запросить этот класс классов, однако ты хочешь. ClassGraph поддерживает больше механизмов спецификации classpath и загрузчиков классов , чем любой другой сканер, а также без проблем работает с новой системой модулей JPMS, поэтому, если вы основываете свой код на ClassGraph, ваш код будет максимально переносимым. См. API здесь.

3 голосов
/ 02 февраля 2017

Я просто пишу простую демонстрацию, чтобы использовать org.reflections.Reflections для получения подклассов абстрактного класса:

https://github.com/xmeng1/ReflectionsDemo

3 голосов
/ 30 января 2009

Причина, по которой вы видите разницу между вашей реализацией и Eclipse, заключается в том, что вы сканируете каждый раз, в то время как Eclipse (и другие инструменты) сканируют только один раз (во время загрузки проекта большую часть времени) и создают индекс. В следующий раз, когда вы запросите данные, они не сканируются снова, но посмотрите на индекс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...