Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reflections lib for Java: find all classes in a package

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?

like image 408
mtrc Avatar asked Sep 19 '12 16:09

mtrc


2 Answers

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.

like image 103
gigadot Avatar answered Nov 15 '22 00:11

gigadot


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.

like image 29
vdimitrov Avatar answered Nov 14 '22 23:11

vdimitrov