你如何在 Java 中找到给定类的所有子类?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/492184/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 15:19:12  来源:igfitidea点击:

How do you find all subclasses of a given class in Java?

javaclassinterfacesubclass

提问by Avrom

How does one go about and try to find all subclasses of a given class (or all implementors of a given interface) in Java? As of now, I have a method to do this, but I find it quite inefficient (to say the least). The method is:

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

  1. Get a list of all class names that exist on the class path
  2. Load each class and test to see if it is a subclass or implementor of the desired class or interface
  1. 获取类路径上存在的所有类名的列表
  2. 加载每个类并测试它是否是所需类或接口的子类或实现者

In Eclipse, there is a nice feature called the Type Hierarchy that manages to show this quite efficiently. How does one go about and do it programmatically?

在 Eclipse 中,有一个很好的特性,称为类型层次结构,可以非常有效地展示这一点。如何以编程方式进行?

采纳答案by matt b

There is no other way to do it other than what you described. Think about it - how can anyone know what classes extend ClassX without scanning each class on the classpath?

除了您所描述的之外,没有其他方法可以做到这一点。想一想 - 没有人扫描类路径上的每个类,怎么能知道哪些类扩展了 ClassX?

Eclipse can only tell you about the super and subclasses in what seems to be an "efficient" amount of time because it already has all of the type data loaded at the point where you press the "Display in Type Hierarchy" button (since it is constantly compiling your classes, knows about everything on the classpath, etc).

Eclipse 只能在看似“有效”的时间内告诉您有关超类和子类的信息,因为它已经在您按下“在类型层次结构中显示”按钮时加载了所有类型数据(因为它是不断编译您的类,了解类路径上的所有内容等)。

回答by Mark Renouf

This is not possible to do using only the built-in Java Reflections API.

仅使用内置的 Java 反射 API 无法做到这一点。

A project exists that does the necessary scanning and indexing of your classpath so you can get access this information...

存在一个项目,它对您的类路径进行必要的扫描和索引,以便您可以访问此信息...

Reflections

反思

A Java runtime metadata analysis, in the spirit of Scannotations

Reflections scans your classpath, indexes the metadata, allows you to query it on runtime and may save and collect that information for many modules within your project.

Using Reflections you can query your metadata for:

  • get all subtypes of some type
  • get all types annotated with some annotation
  • get all types annotated with some annotation, including annotation parameters matching
  • get all methods annotated with some

一个 Java 运行时元数据分析,本着 Scannotations 的精神

反射扫描你的类路径,索引元数据,允许你在运行时查询它,并可以保存和收集项目中许多模块的信息。

使用反射,您可以查询元数据:

  • 获取某种类型的所有子类型
  • 使用一些注释来注释所有类型
  • 获取所有类型的注解,包括注解参数匹配
  • 用一些注释所有方法

(disclaimer: I have not used it, but the project's description seems to be an exact fit for your needs.)

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

回答by Andrzej Doyle

It should be noted as well that this will of course only find all those subclasses that exist on your current classpath. Presumably this is OK for what you are currently looking at, and chances are you did consider this, but if you have at any point released a non-finalclass into the wild (for varying levels of "wild") then it is entirely feasible that someone else has written their own subclass that you will not know about.

还应该注意的是,这当然只会找到当前类路径中存在的所有子类。据推测,这对于您目前正在查看的内容来说是可以的,并且您可能确实考虑过这一点,但是如果您在任何时候将非final职业发布到野外(对于不同级别的“野生”),那么完全可行的是其他人编写了他们自己的子类,您不会知道。

Thus if you happened to be wanting to see all subclasses because you want to make a change and are going to see how it affects subclasses' behaviour - then bear in mind the subclasses that you can't see. Ideally all of your non-private methods, and the class itself should be well-documented; make changes according to this documentation without changing the semantics of methods/non-private fields and your changes should be backwards-compatible, for any subclass that followed your definition of the superclass at least.

因此,如果您碰巧想要查看所有子类,因为您想进行更改并且要查看它如何影响子类的行为 - 那么请记住您看不到的子类。理想情况下,您的所有非私有方法以及类本身都应该有详细记录;根据本文档进行更改而不更改方法/非私有字段的语义,并且您的更改应该向后兼容,至少对于遵循您的超类定义的任何子类。

回答by Rob

Don't forget that the generated Javadocfor a class will include a list of known subclasses (and for interfaces, known implementing classes).

不要忘记为一个类生成的Javadoc将包含一个已知子类的列表(对于接口,已知实现类)。

回答by OscarRyz

The reason you see a difference between your implementation and Eclipse is because you scan each time, while Eclipse (and other tools) scan only once (during project load most of the times) and create an index. Next time you ask for the data it doesn't scan again, but look at the index.

您看到您的实现和 Eclipse 之间存在差异的原因是因为您每次都进行扫描,而 Eclipse(和其他工具)仅扫描一次(大多数情况下在项目加载期间)并创建索引。下次您请求数据时,它不会再次扫描,而是查看索引。

回答by fforw

Scanning for classes is not easy with pure Java.

使用纯 Java 扫描类并不容易。

The spring framework offers a class called ClassPathScanningCandidateComponentProviderthat can do what you need. The following example would find all subclasses of MyClass in the package org.example.package

spring 框架提供了一个名为ClassPathScanningCandidateComponentProvider的类,它可以满足您的需求。以下示例将在包 org.example.package 中查找 MyClass 的所有子类

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
}

This method has the additional benefit of using a bytecode analyzer to find the candidates which means it will notload all classes it scans.

此方法具有使用字节码分析器查找候选对象的额外好处,这意味着它不会加载它扫描的所有类。

回答by Curtis

I know I'm a few years late to this party, but I came across this question trying to solve the same problem. You can use Eclipse's internal searching programatically, if you're writing an Eclipse Plugin (and thus take advantage of their caching, etc), to find classes which implement an interface. Here's my (very rough) first cut:

我知道我参加这个聚会晚了几年,但我遇到了这个问题,试图解决同样的问题。如果您正在编写 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();
    }
  }

The first problem I see so far is that I'm only catching classes which directly implement the interface, not all their subclasses - but a little recursion never hurt anyone.

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

回答by David Leppik

I did this several years ago. The most reliable way to do this (i.e. with official Java APIs and no external dependencies) is to write a custom doclet to produce a list that can be read at runtime.

我几年前就这样做了。执行此操作的最可靠方法(即使用官方 Java API 且没有外部依赖项)是编写自定义 doclet 以生成可在运行时读取的列表。

You can run it from the command line like this:

您可以像这样从命令行运行它:

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

or run it from ant like this:

或者像这样从 ant 运行它:

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

Here's the basic code:

这是基本代码:

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;
        }
    }
}

For simplicity, I've removed command line argument parsing and I'm writing to System.out rather than a file.

为简单起见,我删除了命令行参数解析,我正在写入 System.out 而不是文件。

回答by miko?ak

Keeping in mind the limitations mentioned in the other answers, you can also use openpojo's PojoClassFactory(available on Maven) in the following manner:

请记住其他答案中提到的限制,您还可以通过以下方式使用openpojoPojoClassFactory在 Maven 上可用):

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

Where packageRootis the root String of the packages you wish to search in (e.g. "com.mycompany"or even just "com"), and Superclassis your supertype (this works on interfaces as well).

packageRoot您希望搜索的包的根字符串在哪里(例如"com.mycompany",甚至只是"com"),并且Superclass是您的超类型(这也适用于接口)。

回答by Ravindranath Akila

Add them to a static map inside (this.getClass().getName()) the parent classes constructor (or create a default one) but this will get updated in runtime. If lazy initialization is an option you can try this approach.

将它们添加到 (this.getClass().getName()) 父类构造函数(或创建默认构造函数)内的静态映射,但这将在运行时更新。如果延迟初始化是一个选项,您可以尝试这种方法。