如何在Java中找到给定类的所有子类(或给定接口的所有实现者)? 到目前为止,我有一个方法来做到这一点,但我发现它相当低效(至少可以说)。 方法是:
获取类路径上存在的所有类名的列表 加载每个类并测试它是否是所需类或接口的子类或实现者
在Eclipse中,有一个很好的特性叫做类型层次结构,它能够非常有效地显示这一点。 如何以编程的方式进行呢?
如何在Java中找到给定类的所有子类(或给定接口的所有实现者)? 到目前为止,我有一个方法来做到这一点,但我发现它相当低效(至少可以说)。 方法是:
获取类路径上存在的所有类名的列表 加载每个类并测试它是否是所需类或接口的子类或实现者
在Eclipse中,有一个很好的特性叫做类型层次结构,它能够非常有效地显示这一点。 如何以编程的方式进行呢?
当前回答
你可以使用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)));
其他回答
Try ClassGraph. (Disclaimer, I am the author). ClassGraph supports scanning for subclasses of a given class, either at runtime or at build time, but also much more. ClassGraph can build an abstract representation of the entire class graph (all classes, annotations, methods, method parameters, and fields) in memory, for all classes on the classpath, or for classes in selected packages, and you can query this class graph however you want. ClassGraph supports more classpath specification mechanisms and classloaders than any other scanner, and also works seamlessly with the new JPMS module system, so if you base your code on ClassGraph, your code will be maximally portable. See the API here.
如果你想加载给定类在同一个包中的所有子类,你可以这样做:
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;
}
用纯Java扫描类并不容易。
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
}
该方法还有一个额外的好处,就是使用字节码分析器来查找候选类,这意味着它不会加载所扫描的所有类。
我只是写了一个简单的演示来使用org.reflections.Reflections来获取抽象类的子类:
https://github.com/xmeng1/ReflectionsDemo
几年前我就这样做了。最可靠的方法(即使用官方Java api,没有外部依赖)是编写一个自定义doclet来生成一个可以在运行时读取的列表。
你可以像这样从命令行运行它:
javadoc -d build -doclet com.example.ObjectListDoclet -sourcepath java/src -subpackages com.example
或者像这样从ant运行它:
<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而不是file。