如何在Java中尝试查找给定类(或给定接口的所有实现者)的所有子类? 截至目前,我有一种方法可以做到这一点,但我觉得效率很低(至少可以说)。 方法是:
在Eclipse中,有一个很好的功能叫做类型层次结构,可以非常有效地显示它。 如何进行编程并以编程方式执行?
答案 0 :(得分:118)
使用纯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
}
此方法的另一个好处是使用字节码分析器查找候选项,这意味着它不加载它扫描的所有类。
答案 1 :(得分:69)
除了你描述的内容之外别无他法。想一想 - 如何在不扫描类路径上的每个类的情况下知道什么类扩展ClassX?
Eclipse只能告诉你超级和子类似乎是一个“有效”的时间,因为它已经在你按下“在类型层次结构中显示”按钮的位置加载了所有类型数据(因为它不断编译你的类,知道类路径上的所有内容,等等。)
答案 2 :(得分:46)
仅使用内置的Java Reflections API无法做到这一点。
存在一个项目,它对您的类路径进行必要的扫描和索引,以便您可以访问此信息...
本着Scannotations
的精神进行Java运行时元数据分析 Reflections会扫描您的类路径,索引元数据,允许您在运行时查询它,并可以为项目中的许多模块保存和收集该信息。使用Reflections,您可以查询元数据:
- 获取某种类型的所有子类型
- 获取所有注释了一些注释的类型
- 获取所有注释了一些注释的类型,包括匹配的注释参数
- 获取所有使用某些
注释的方法
(免责声明:我没有使用它,但该项目的描述似乎完全符合您的需求。)
答案 3 :(得分:10)
不要忘记,为类生成的Javadoc将包含已知子类的列表(以及接口,已知的实现类)。
答案 4 :(得分:8)
您可以从命令行运行它,如下所示:
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而不是文件。
答案 5 :(得分:7)
我知道我参加这个聚会已经晚了几年,但我遇到了这个试图解决同样问题的问题。您可以以编程方式使用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();
}
}
我到目前为止看到的第一个问题是我只捕获直接实现接口的类,而不是所有子类 - 但是一点点递归从不会伤害任何人。
答案 6 :(得分:7)
请记住其他答案中提到的限制,您也可以通过以下方式使用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
是您的超类型(这是有效的)在接口上也是如此)。
答案 7 :(得分:6)
试试ClassGraph。 (免责声明,我是作者)。 ClassGraph支持在运行时或构建时扫描给定类的子类,但也支持更多。 ClassGraph可以在内存中为类路径上的所有类或白名单包中的类构建整个类图(所有类,注释,方法,方法参数和字段)的抽象表示,然后您可以查询该类图你要。 ClassGraph比任何其他扫描程序都支持more classpath specification mechanisms and classloaders,并且还可以与新的JPMS模块系统无缝协作,因此如果您将代码基于ClassGraph,您的代码将最大程度地可移植。 See the API here.
答案 8 :(得分:3)
应该注意的是,这当然只能找到当前类路径中存在的所有子类。大概这对你目前正在看的东西是可以的,你可能会考虑到这一点,但如果你在任何时候发布了一个非final
类到野外(对于不同级别的“野性”)那么其他人编写了你自己不了解的子类是完全可行的。
因此,如果您碰巧想要查看所有子类,因为您想要进行更改并且要查看它如何影响子类的行为 - 那么请记住您看不到的子类。理想情况下,所有非私有方法和类本身都应该有详细记录;根据本文档进行更改而不更改方法/非私有字段的语义,并且对于任何至少遵循超类定义的子类,您的更改应该是向后兼容的。
答案 9 :(得分:3)
您看到实现与Eclipse之间存在差异的原因是因为您每次扫描,而Eclipse(和其他工具)只扫描一次(在项目加载期间大多数时间)并创建索引。下次您要求数据时,它不会再次扫描,但请查看索引。
答案 10 :(得分:3)
我只是编写一个简单的演示来使用HtmlControl control = Master.FindControl("nbGallery") as HtmlControl;
control.Attributes.Add("class", "active");
来获取抽象类的子类:
答案 11 :(得分:2)
我使用了一个反射库,它会扫描你的类路径中的所有子类:https://github.com/ronmamo/reflections
这是如何做到的:
Reflections reflections = new Reflections("my.project");
Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);
答案 12 :(得分:2)
根据您的特定要求,在某些情况下,Java service loader机制可能会实现您的目标。
简而言之,它允许开发人员通过将其列在JAR / WAR文件的META-INF/services
目录中的文件中来显式声明某个类是否为其他类(或实现某个接口)的子类。然后可以使用java.util.ServiceLoader
类来发现它,当给定Class
对象时,它将生成该类的所有已声明子类的实例(或者,如果Class
表示接口,所有实现该接口的类。)
这种方法的主要优点是不需要为子类手动扫描整个类路径 - 所有发现逻辑都包含在ServiceLoader
类中,并且它只加载META-INF/services
中显式声明的类。 {1}}目录(不是类路径上的每个类)。
然而,有一些缺点:
META-INF/services
目录下显式声明该类。这对开发人员来说是一个额外的负担,并且可能容易出错。ServiceLoader.iterator()
生成子类实例,而不是Class
个对象。这导致两个问题:
显然Java 9将解决其中一些缺点(特别是关于子类实例化的缺点)。
假设您对查找实现接口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
是com.example.ExampleImpl
的实施。
然后,您可以获得Example
的每个实现的实例(包括ExampleImpl
的实例),如下所示:
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.
答案 13 :(得分:1)
将它们添加到静态地图里面(this.getClass()。getName())父类构造函数(或创建一个默认值),但这将在运行时更新。如果延迟初始化是一个选项,您可以尝试这种方法。
答案 14 :(得分:1)
您可以使用org.reflections库,然后创建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)));
答案 15 :(得分:0)
我需要将此作为测试用例,以查看是否已将新类添加到代码中。这就是我所做的
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 :(得分:0)
如果打算加载同一包中给定类的所有子类,则可以这样做:
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;
}
答案 17 :(得分: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;
}