Reputation: 1345
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?
Upvotes: 7
Views: 7285
Reputation: 1062
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"))));
Upvotes: 1
Reputation: 1111
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.
Upvotes: 3
Reputation: 8969
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.
Upvotes: 6