如何在Java中查找给定类的所有子类(或给定接口的所有实现者)?
目前,我有一种方法来完成这个任务,但我发现它相当低效(至少可以这么说)。
该方法是:
- 获取类路径上存在的所有类名的列表
- 加载每个类并测试以查看它是否是所需类或接口的子类或实现者
在Eclipse中,有一个很好的功能称为Type Hierarchy可以高效地显示此信息。
如何以编程方式完成这项工作?
如何在Java中查找给定类的所有子类(或给定接口的所有实现者)?
目前,我有一种方法来完成这个任务,但我发现它相当低效(至少可以这么说)。
该方法是:
在Eclipse中,有一个很好的功能称为Type Hierarchy可以高效地显示此信息。
如何以编程方式完成这项工作?
你看到你的实现和Eclipse之间有差异,是因为你每次都要扫描,而Eclipse(和其他工具)只扫描一次(在大多数情况下在项目加载期间)并创建索引。下次您请求数据时,它不会再次扫描,而是查看索引。
我正在使用一个反射库,它会扫描你的类路径以获取所有子类:https://github.com/ronmamo/reflections
下面是具体的操作:
Reflections reflections = new Reflections("my.project");
Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);
Reflections reflections = new Reflections("my.project.prefix");
System.out.println(reflections.getSubTypesOf(A.class)));
将它们添加到父类的构造函数内部的静态映射中(使用(this.getClass().getName())),或创建一个默认的构造函数,但这将在运行时更新。如果惰性初始化是一种选择,则可以尝试此方法。
我刚写了一个简单的演示,使用org.reflections.Reflections
来获取抽象类的子类:
我需要做这个测试案例,以查看代码是否添加了新类。这是我的做法。
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());
}
}
}
如果你想加载同一个包中的给定类的所有子类,可以这样做:
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;
}
查找类路径中的所有类
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;
}