如何在Java中找到给定类的所有子类(或给定接口的所有实现者)? 到目前为止,我有一个方法来做到这一点,但我发现它相当低效(至少可以说)。 方法是:

获取类路径上存在的所有类名的列表 加载每个类并测试它是否是所需类或接口的子类或实现者

在Eclipse中,有一个很好的特性叫做类型层次结构,它能够非常有效地显示这一点。 如何以编程的方式进行呢?


当前回答

仅使用内置的Java Reflections API是不可能做到这一点的。

存在一个项目,它对你的类路径进行必要的扫描和索引,这样你就可以访问这些信息……

反射

Java运行时元数据分析,遵循Scannotations的精神 Reflections扫描您的类路径,索引元数据,允许您在运行时查询它,并可以为项目中的许多模块保存和收集这些信息。 使用Reflections你可以查询你的元数据: 获取某个类型的所有子类型 用一些注释来注释所有类型 获取使用某些注释注释的所有类型,包括注释参数匹配 让所有方法都带有一些注解

(免责声明:我没有使用过它,但项目的描述似乎完全符合您的需求。)

其他回答

除了你所描述的,没有别的办法了。想想看——如果不扫描类路径上的每个类,如何知道哪些类扩展了ClassX ?

Eclipse只能在看起来“有效”的时间内告诉您关于父类和子类的信息,因为在您按下“显示在类型层次结构中”按钮时,它已经加载了所有类型数据(因为它一直在编译您的类,知道类路径上的所有内容,等等)。

我需要将此作为一个测试用例,以查看是否向代码中添加了新的类。这就是我所做的

final static File rootFolder = new File(SuperClass.class.getProtectionDomain().getCodeSource().getLocation().getPath());
private static ArrayList<String> files = new ArrayList<String>();
listFilesForFolder(rootFolder); 

@Test(timeout = 1000)
public void testNumberOfSubclasses(){
    ArrayList<String> listSubclasses = new ArrayList<>(files);
    listSubclasses.removeIf(s -> !s.contains("Superclass.class"));
    for(String subclass : listSubclasses){
        System.out.println(subclass);
    }
    assertTrue("You did not create a new subclass!", listSubclasses.size() >1);     
}

public static void listFilesForFolder(final File folder) {
    for (final File fileEntry : folder.listFiles()) {
        if (fileEntry.isDirectory()) {
            listFilesForFolder(fileEntry);
        } else {
            files.add(fileEntry.getName().toString());
        }
    }
}

我知道我在这个派对上晚了几年,但我遇到这个问题是为了解决同样的问题。如果您正在编写Eclipse Plugin(从而利用它们的缓存等),您可以通过编程方式使用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();
    }
  }

到目前为止,我看到的第一个问题是,我只捕获直接实现接口的类,而不是它们的所有子类——但一点递归不会伤害任何人。

记住其他答案中提到的限制,你也可以以以下方式使用openpojo的PojoClassFactory(在Maven上可用):

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

packageRoot是你想要搜索的包的根字符串。“com。mycompany”或者只是“com”),超类是你的超类型(这也适用于接口)。

您看到您的实现和Eclipse之间的区别的原因是您每次都扫描,而Eclipse(和其他工具)只扫描一次(大多数时候在项目加载期间)并创建索引。下次你请求数据时,它不再扫描,而是查看索引。