11

If I have

Reflections reflections = new Reflections("my.package", classLoader, new SubTypesScanner(false));

then this finds my enum classes

Set<Class<? extends Enum>> enums = reflections.getSubTypesOf(Enum.class);

but this doesn't

Set<Class<?>> classes = reflections.getSubTypesOf(Object.class);

Is there a reason for this?


Reproducible example:

package cupawntae;

import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;

public class Reflector {
    public static void main(String[] args) {
        Reflections reflections = new Reflections("cupawntae", Reflector.class.getClassLoader(), new SubTypesScanner(false));
        System.out.println("Objects: " + reflections.getSubTypesOf(Object.class));
        System.out.println("Enums: " + reflections.getSubTypesOf(Enum.class));
        System.out.println("Enum's superclass: " + Enum.class.getSuperclass());
    }
}

Enum class:

package cupawntae;

public enum MyEnum {
}

Output:

Objects: [class cupawntae.Reflector]
Enums: [class cupawntae.MyEnum]
Enum's superclass: class java.lang.Object
CupawnTae
  • 14,192
  • 3
  • 29
  • 60
  • 1
    Looks like a bug. Note that if the enum implements any interface, it suddenly gets found. Until the bug gets fixed, you can use e.g. `ClassPath.from(ClassLoader.getSystemClassLoader()).getTopLevelClasses("cupawntae")` of Google Guava. Not as slick, but gets the job done. – Petr Janeček Feb 23 '16 at 19:18
  • 1
    Thanks but looks like it's documented - although that's weird about the interface thing. How did you discover that? Did you by any chance implement an interface inside your target package? – CupawnTae Feb 23 '16 at 21:24

1 Answers1

12

This is actually documented behaviour, although it's arguably not particularly clear or intuitive:

be aware that when using the constructor new Reflections("my.package"), only urls with prefix 'my.package' will be scanned, and any transitive classes in other urls will not be scanned (for example if my.package.SomeClass extends other.package.OtherClass, than the later will not be scanned). in that case use the other constructors and specify the relevant packages/urls

edit: later revision of that doc says:

Make sure to scan all the transitively relevant packages. for instance, given your class C extends B extends A, and both B and A are located in another package than C, when only the package of C is scanned - then querying for sub types of A returns nothing (transitive), but querying for sub types of B returns C (direct). In that case make sure to scan all relevant packages a priori.

In this case java.lang.Enum counts as a transitive class (like other.package.OtherClass), and is therefore not included in the scan, meaning subclasses of Enum are not included.

Similarly, if we make Reflections in the question's example extend something outside the target package, e.g.

public class Reflector extends Exception {

then the class is no longer found in the scan

Objects: []
Enums: [class cupawntae.MyEnum]
Enum's superclass: class java.lang.Object
CupawnTae
  • 14,192
  • 3
  • 29
  • 60
  • 2
    Wow, that's awful. So if you'll have a specialized class extending `AbstractList`, or your own Exception, as you showed, then `reflections.getAllTypes()` will NOT find it. What a mess. Even if that's intended, it should be changed somehow. At least it should go over java.lang and java.util automatically! – Petr Janeček Feb 23 '16 at 22:36
  • 2
    Oh my god. Even if you specifically include `java.lang` and `java.util` as packages, neither `reflections.getAllTypes()` nor `reflections.getSubTypesOf(Object.class)` will give you the specialized Exception/LIst. So there's just no way to get all your classes with this library, is there? What a shame. – Petr Janeček Feb 23 '16 at 22:47
  • 1
    Okay, last comment and I'll be done. Outside the already mentioned Guava's `ClassPath`, there's https://github.com/lukehutch/fast-classpath-scanner that does this as well. I haven't tried it, but it looks pretty mature. – Petr Janeček Feb 23 '16 at 22:52
  • 1
    Gotta look at this stuff some more tomorrow - thanks for the pointers, and if I find anything else useful I'll update – CupawnTae Feb 23 '16 at 23:49
  • 2
    Juan Garcia, if you see this, thanks for the edit. In fact the one you found was newer, and had been reworded slightly (not sure if it reflected (no pun intended) a change in behaviour, didn't read in enough detail). Anyway, I just tried changing the `10` to a `9` on the off-chance it would find the version I quoted, and what do you know? It's there! Cheers – CupawnTae Dec 17 '16 at 22:47
  • No problem. And also thanks a lot for updating the answer. – Juan Garcia Dec 18 '16 at 06:36
  • Instead of `reflections.getAllTypes()` you can use `reflections.getStore().getAll(SubTypesScanner.class, reflections.getStore().keys(SubTypesScanner.class.getSimpleName()))` instead. What a mess. – Jan-Willem Gmelig Meyling Dec 28 '20 at 17:22