代码之家  ›  专栏  ›  技术社区  ›  Avrom

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

  •  181
  • Avrom  · 技术社区  · 17 年前

    到目前为止,我有一种方法可以做到这一点,但我发现它效率很低(至少可以这么说)。 方法是:

    1. 获取类路径上存在的所有类名的列表

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

    15 回复  |  直到 16 年前
        1
  •  134
  •   fforw    8 年前

    用纯Java扫描类并不容易。

    spring框架提供了一个名为 ClassPathScanningCandidateComponentProvider

    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
    }
    

    这种方法还有一个额外的好处,就是使用字节码分析器来查找候选字符,这意味着它将 加载它扫描的所有类。

        2
  •  80
  •   matt b    17 年前

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

        3
  •  49
  •   Community Mohan Dere    6 年前

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

    Reflections

    Java运行时元数据分析,以Scannotations为精神

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

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

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

        4
  •  13
  •   Luke Hutchison    5 年前

    ClassGraph . (免责声明,我是作者)。ClassGraph支持在运行时或构建时扫描给定类的子类,但也支持更多。ClassGraph可以在内存中为类路径上的所有类或选定包中的类构建整个类图(所有类、注释、方法、方法参数和字段)的抽象表示,并且您可以根据需要查询此类图。类图支持 more classpath specification mechanisms and classloaders 与任何其他扫描仪相比,它还可以无缝地与新的JPMS模块系统配合使用,所以如果您的代码基于ClassGraph,那么您的代码将具有最大的可移植性。 See the API here.

        5
  •  11
  •   Rob    17 年前

    别忘了这是一个 Javadoc 对于类,将包括已知子类的列表(对于接口,则包括已知的实现类)。

        6
  •  9
  •   Curtis    15 年前

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

        7
  •  9
  •   David Leppik    14 年前

    我几年前就这样做了。实现这一点最可靠的方法(即,使用官方JavaAPI且没有外部依赖)是编写一个自定义doclet,以生成一个可在运行时读取的列表。

    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而不是文件。

        8
  •  7
  •   mikołak    12 年前

    记住其他答案中提到的限制,您也可以使用 openpojo's PojoClassFactory ( available on Maven )以下列方式:

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

    packageRoot 是要搜索的包的根字符串(例如。 "com.mycompany" 甚至只是 "com" ),及 Superclass

        9
  •  4
  •   Xin Meng    9 年前

    我只是写了一个简单的演示来使用 org.reflections.Reflections

    https://github.com/xmeng1/ReflectionsDemo

        10
  •  4
  •   Mac Umer    8 年前

    根据您的特定需求,在某些情况下,Java的 service loader 这个机制可能会实现你所追求的目标。

    META-INF/services 目录然后可以使用 java.util.ServiceLoader 类,当给定 Class 表示一个接口(实现该接口的所有类)。

    这种方法的主要优点是不需要手动扫描整个类路径中的子类——所有发现逻辑都包含在 ServiceLoader 类,并且它只加载在 META-INF/服务 目录(不是类路径上的每个类)。

    • 它找不到 全部的
    • 它要求开发人员在 META-INF/服务 目录这对开发人员来说是一个额外的负担,并且容易出错。
    • ServiceLoader.iterator() 生成子类实例,而不是它们的
      • 对于如何构造子类,您没有任何发言权-无参数构造函数用于创建实例。

    显然,Java9将解决其中一些缺点(特别是关于子类实例化的缺点)。

    一个例子

    假设您对查找实现接口的类感兴趣 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 .

    实例 (包括 示例impl )详情如下:

    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.
    
        11
  •  3
  •   Andrzej Doyle    17 年前

    final 类进入野生状态(对于不同级别的“野生状态”),那么完全有可能其他人编写了自己的子类,而您对此一无所知。

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

        12
  •  3
  •   Peter Mortensen Pieter Jan Bonestroo    14 年前

    您之所以看到实现与Eclipse之间的差异,是因为您每次都扫描,而Eclipse(和其他工具)只扫描一次(在大多数项目加载期间)并创建索引。下次你要数据时,它不会再扫描,而是查看索引。

        13
  •  3
  •   futchas    8 年前

    我使用的是一个反射库,它扫描您的类路径以查找所有子类: https://github.com/ronmamo/reflections

    这是如何做到的:

    Reflections reflections = new Reflections("my.project");
    Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);
    
        14
  •  2
  •   Ravindranath Akila    11 年前

        15
  •  1
  •   Cutetare    8 年前

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

    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());
            }
        }
    }
    
        16
  •  1
  •   P Mittal    6 年前

    您可以使用org.reflections库,然后创建反射对象类。使用这个对象,您可以得到给定类的所有子类的列表。 https://www.javadoc.io/doc/org.reflections/reflections/0.9.10/org/reflections/Reflections.html

        Reflections reflections = new Reflections("my.project.prefix");
        System.out.println(reflections.getSubTypesOf(A.class)));
    
        17
  •  0
  •   Lyfing    5 年前

    如果要加载同一包中给定类的所有子类,可以执行以下操作:

    public static List<Class> loadAllSubClasses(Class pClazz) throws IOException, ClassNotFoundException {
        ClassLoader classLoader = pClazz.getClassLoader();
        assert classLoader != null;
        String packageName = pClazz.getPackage().getName();
        String dirPath = packageName.replace(".", "/");
        Enumeration<URL> srcList = classLoader.getResources(dirPath);
    
        List<Class> subClassList = new ArrayList<>();
        while (srcList.hasMoreElements()) {
            File dirFile = new File(srcList.nextElement().getFile());
            File[] files = dirFile.listFiles();
            if (files != null) {
                for (File file : files) {
                    String subClassName = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
                    if (! subClassName.equals(pClazz.getName())) {
                        subClassList.add(Class.forName(subClassName));
                    }
                }
            }
        }
    
        return subClassList;
    }
    
        18
  •  0
  •   Enaium    5 年前

    public static List<String> getClasses() {
            URLClassLoader urlClassLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
            List<String> classes = new ArrayList<>();
            for (URL url : urlClassLoader.getURLs()) {
                try {
                    if (url.toURI().getScheme().equals("file")) {
                        File file = new File(url.toURI());
                        if (file.exists()) {
                            try {
                                if (file.isDirectory()) {
                                    for (File listFile : FileUtils.listFiles(file, new String[]{"class"}, true)) {
                                        String classFile = listFile.getAbsolutePath().replace(file.getAbsolutePath(), "").replace(".class", "");
                                        if (classFile.startsWith(File.separator)) {
                                            classFile = classFile.substring(1);
                                        }
                                        classes.add(classFile.replace(File.separator, "."));
                                    }
                                } else {
                                    JarFile jarFile = new JarFile(file);
                                    if (url.getFile().endsWith(".jar")) {
                                        Enumeration<JarEntry> entries = jarFile.entries();
                                        while (entries.hasMoreElements()) {
                                            JarEntry jarEntry = entries.nextElement();
                                            if (jarEntry.getName().endsWith(".class")) {
                                                classes.add(jarEntry.getName().replace(".class", "").replace("/", "."));
                                            }
                                        }
                                    }
                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                } catch (URISyntaxException e) {
                    e.printStackTrace();
                }
            }
            return classes;
        }
    
    推荐文章