views:

104

answers:

3

I have a class in Java, I wish to reflect all subclasses of this class, how would I do this?

In this specific case, all subclasses are in the same package, and only subclasses are in this package, so an equivalent solution is to fetch all classes in a package.

+1  A: 

See this answer how to locate all classes in a package.

Next, you'll need a list of all packages which isn't trivial in the general case. But if you only use the standard classloader (which solely relies on the classpath), you can get the system property java.class.path and analyze that to get all JARs. Open them to list the content and then you'll know the class names.

Aaron Digulla
+1  A: 

It is possible to do (messily) in some cases; e.g. if your application's classes are loaded from local directories or JAR files. Basically, you need to map the package name to a pathname and use the File and/or ZipFile APIs to look for files in the relevant "directory" whose name ends with ".class". Then you use Class.forName() load the corresponding classes (trying to avoid initializing them), and use clazz.isAssignableFrom(subclazz) to see which of them are subclasses.

But this won't work in all cases. One problem is that the ClassLoader API does not support iterating over all of the classes / packages that it could load.

I would recommend that you find an alternative approach to the problem you are tying to address.

EDIT - in response to this comment.

It seems this is a massive fault in the java reflection API

I understand that the omission is deliberate. If the classloader API did include a method to give all classes in a package, it would limit the schemes that can be used for classloading. For example, URLClassLoader would not be implementable for 'http:' URLs because the HTTP protocol does not provide a standard way to retrieve the entries in a directory.

Besides, there are few situations where a production application really needs to reflectively find all classes in a package.

Stephen C
In the end I did find an alternative solution, which is a bit messy but it's better than any of the solutions I've read around (like this one). It seems this is a massive fault in the java reflection API :(
Martin
I see your point with the response to my comment. Although you could always subclass Package with "searchablePackage" which does allows listing and searching amongst its classes :P
Martin
@Martin - How would you implement it? The fundamental problem is that it is impossible to *implement* iteration over the loadable classes in a package ... without making certain kinds of class loader unimplementable.
Stephen C
I mean sun could implement that internally, so *some* loaders (which can do it) have the capability exposed, and others (which can't) don't.
Martin
@Martin - so we have a method to return classes that works only for some classloaders? No thanks!
Stephen C
No, we two types of class loader extending the base classloader, "BrowsableClassloader" offers capabilities to getClasses, "UnbrowsableClassloader" does not. So the URL classloader in your example, would extend UnbrowsableClassloader, other ones (where appropriate) would extend BrowsableClassloader. It's a simple use of OOP...
Martin
@Martin - my objection still stands. But anyway, it is not my opinion that matters. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4061452
Stephen C
+1  A: 

I think you could do this using spring's org.springframework.core.io.support.PathMatchingResourcePatternResolver. At least I know you can use it to find all classes with a certain annotation. Finding subclasses seems to be a very similar problem I'd expect to work as well.

Here's some (untested) code to get you started:

PathMatchingResourcePatternResolver match = new PathMatchingResourcePatternResolver();
MetadataReaderFactory f = new SimpleMetadataReaderFactory();
List<Class<?>> matches = ...;
for (Resource r : match.getResources("classpath*:com/example/**/*.class")) {
    AnnotationMetadata meta = f.getMetadataReader(r).getAnnotationMetadata();
    if (anno.getAnnotationsTypes().contains(MyAnnotation.class.getName()) {
        matches.add(Class.forName(anno.getClassName()));
    }
}
return matches;

As you might see, it's basically the idea describe by Stephen C

sfussenegger