7

Further to the question posted here: Can you find all classes in a package using reflection? I started using the Reflections library to find all classes that subclass a given type. The source code looks like this, from an answer to the linked SO question:

Reflections ref = new Reflections(new ConfigurationBuilder() .setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner()) .setUrls(ClasspathHelper.forPackage("org.somepackage")) .filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("org.somepackage")))); ref.getSubtypesOf(Object.class); 

However, after using this code obliviously for a while, I've just discovered that it will only find classes that subclass another type within this package. It won't find classes that subclass externally defined classes, say from another user-defined package.

I'm not sure how to get around this using the Reflections library. I want all classes that declare their package as 'org.somepackage', regardless of what their supertype is. Any help?

3 Answers 3

6

I wrote a library called Rebound (as opposed to Reflections) which searches for the subclasses of a given type and package prefix. If you set the prefix empty, it will search every class under the classpath, e.g.

import gigadot.exp.reflects.core.Processor; Rebound r = new Rebound(""); Set<Class<? extends Processor>> classes = r.getSubClassesOf(Processor.class); 

But you should be careful, because searching everything in the classpath is a slow process.

The library is much simpler than Reflections and might not do what you want. I wrote this due to my frustration when I submitted my bug report but no one there tries to solve the problem.

Sign up to request clarification or add additional context in comments.

5 Comments

This looks very promising. I don't need to perform many of these calls, but it's crucial that they're completely exhaustive. Thanks a bundle, I'll look into this.
EDIT - Are Rebound's dependencies listed anywhere, e.g. log4j?
Right, got this working - works exactly as needed. Thanks for doing this, you really saved the day. I can go home now!
No problem. You could send a bug report on that site. i will try to solve it as best as i can.
You sir deserve my respect.
3

The reason for this is, that

ref.getSubtypesOf(Object.class); 

only returns the direct subclasses of Object. If you want to get all the classes from a scanned package, you should do the following:

Reflections ref = new Reflections(new ConfigurationBuilder().setScanners(new SubTypesScanner(false), new ResourcesScanner(), new TypeElementsScanner()) ... Set<String> typeSet = reflections.getStore().getStoreMap().get("TypeElementsScanner").keySet(); HashSet<Class<? extends Object>> classes = Sets.newHashSet(ReflectionUtils.forNames(typeSet, reflections .getConfiguration().getClassLoaders())); 

This may look a little hackish but it's the only way I found so far. Here's a little explanation of what this does: When Reflections is done with the scanning, it puts all the elements in a multi-value map. In the code example that I pasted, the results are put inside a map with the following keys:

SubTypesScanner, ResourcesScanner, TypeElementsScanner 

The ResourceScanner excludes all files ending with .class. The TypeElementsScanner is a map with a key holding the name of a class, and a value of the fields etc. So if you want to get only the class names, you basically get the key set and later on, convert it to a set if classes. The SubTypesScanner is also a map, with a key of all the super classes (including Object and interfaces) and values - the classes implementing/extending those interfaces/classes.

You can also use the SubTypesScanner if you wish, by iterating the keyset and getting all the values, but if a certain class implements an interface, you will have to deal with duplicate entities (as every class extends Object).

Hope that helps.

2 Comments

Don't know why nobody tried/gave feedback on this approach, it worked nicely for me. Thanks!
The HashSet<Class<? extends Object>> classes is empty(not sure why), but Set<String> typeSet contains the right class names.
1

In my case, this code works fine to load the classes of an external API, but not sure why it does not work for a built-in package like 'java.util'.

Reflections reflections = new Reflections(new ConfigurationBuilder() .setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner()) .setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[2]))) .filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("org.reflections")))); 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.