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

238

如何在Java中查找给定类的所有子类(或给定接口的所有实现者)?

目前,我有一种方法来完成这个任务,但我发现它相当低效(至少可以这么说)。

该方法是:

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

在Eclipse中,有一个很好的功能称为Type Hierarchy可以高效地显示此信息。

如何以编程方式完成这项工作?


2
虽然基于Reflections和Spring的解决方案看起来很有趣,但我需要一些简单的解决方案,它没有依赖关系。看来我的原始代码(稍加调整)是正确的选择。 - Avrom
1
你肯定可以递归地使用getSuperClass方法吧? - user723720
我特别想找到给定类的所有子类。getSuperClass不能告诉你一个类有哪些子类,只能获取特定子类的直接超类。此外,Class上的isAssignableFrom方法更适合您建议的内容(无需递归)。 - Avrom
2
这个问题与许多其他重复问题有关联,但它并不包含任何有用的、纯Java的答案。唉... - Eric Duminil
@EricDuminil 请看Lyfing的回答。使用纯Java,受限搜索。正是我想要的。 - Art Swri
19个回答

3

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


3

我正在使用一个反射库,它会扫描你的类路径以获取所有子类:https://github.com/ronmamo/reflections

下面是具体的操作:

Reflections reflections = new Reflections("my.project");
Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);

3

2

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


2

3
如果您在此处发布代码示例而不是链接和大量类别,那将更具生产力。 - JesseBoyd
特别是考虑到实际代码只有一两行,就像其他答案中所示。 - Matthew Read

1

我需要做这个测试案例,以查看代码是否添加了新类。这是我的做法。

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

1

如果你想加载同一个包中的给定类的所有子类,可以这样做:

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

1
正是我想要的 - 限定“搜索区域”的简化案例。我想找到一个抽象基类的所有子类,它们都在同一个包中。这样可以“找到”所有的子类并管理它们(在我的情况下添加/注册它们)。谢谢! - Art Swri

0

查找类路径中的所有类

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

(URLClassLoader)Thread.currentThread()。getContextClassLoader(); 如果ClassLoader不是URLClassLoader怎么办?信不信由你,不是每个ClassLoader都是。 - VGR
@VGR System.getProperty("java.class.path") - Enaium
应用程序可以创建ClassLoader,通过其他方式加载类,而不是使用java.class.path属性。 - VGR

-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接