4

I know Class.getDeclaredClasses() can obtains all the classes that it has declared but doesn't including anonymous classes.

I'd like to know is there a way to get all the enclosed classes via the enclosing class ? for example, I want to get the all enclosed classes defined in Root for test purpose.

class Root{
   void run(){
      Runnable task = new Runnable(){
         public void run(){}
      };

      task.getClass().getEnclosingClass();// return Root.class
      // but I want to get all enclosed class via Root.class, for example:
     // Root.class... == task.getClass() 
   }
}

the expected result is : [class of task].

Daniel Nugent
  • 43,104
  • 15
  • 109
  • 137
holi-java
  • 29,655
  • 7
  • 72
  • 83
  • 2
    `task` is the name of a local variable, I do not think there is any way to get that. Or what exactly do you want to achieve? – luk2302 Jun 10 '17 at 15:45
  • @luk2302 but `task` is defined in `Root`, and compiler will generates an additional class for the anonymous class. – holi-java Jun 10 '17 at 15:47
  • Will `getDeclaredClasses()` do it? I haven't used it. Ah, no, apparently that's what you were asking about, but you left off the `es` – David Conrad Jun 10 '17 at 15:49
  • 1
    @DavidConrad `getDeclaredClasses()` return all declared classes except anonymous classes. – holi-java Jun 10 '17 at 15:50
  • 1
    What is your need, your use case? Could this possibly be an [XY Problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) in disguise? – Hovercraft Full Of Eels Jun 10 '17 at 15:52
  • @HovercraftFullOfEels I start to kotlin [inline function](https://kotlinlang.org/docs/reference/inline-functions.html). and I need to test that an inline function is inline to call-site function. so I need get all enclosed class to test that the call site function doesn't generates by compiler. – holi-java Jun 10 '17 at 15:55
  • Looking over the API, I can't see any way to do it other than to guess at anonymous class names as in `Class.forName("Root$1")`, `Class.forName("Root$2")`, etc., but that's not a good way to go about it. – David Conrad Jun 10 '17 at 16:00
  • @HovercraftFullOfEels thanks,sir. the idea look like fine. but I think it isn't for kotlin, just for test purpose. – holi-java Jun 10 '17 at 16:04
  • @DavidConrad thanks, sir. I did that and it works fine for me. – holi-java Jun 10 '17 at 16:55

1 Answers1

5

If you know the naming scheme of your anonymous classes, you can try to load it with Root's ClassLoader:

Naming scheme for javac is <enclosing_class_name>$<anonymous_class_number>:

Class<?> enclosing = Root.class;

try{
    Class<?> anon1 = enclosing.getClassLoader().loadClass(enclosing.getName() + "$1");
    System.out.println(anon1); // prints: class Root$1
} catch (ClassNotFoundException e) {
    System.out.println("no anonymous classes");
}
Jorn Vernee
  • 31,735
  • 4
  • 76
  • 93
  • 3
    Note that while the naming scheme is specified, the actual number is not. https://docs.oracle.com/javase/specs/jls/se8/html/jls-13.html#jls-13.1 While unlikely, it would be legal for e.g. the numbers to be random or start at some number other than 1. So this works, but it doesn't necessarily have to work. – Radiodef Jun 10 '17 at 16:03
  • thank you sir. I try it at once and close the question as soon as possible. – holi-java Jun 10 '17 at 16:05
  • It might be better to use `findLoadedClass` instead of `loadClass` – David Conrad Jun 10 '17 at 16:06
  • @DavidConrad `loadClass` already does that. – Jorn Vernee Jun 10 '17 at 16:07
  • @Radiodef You have a good point, I'm trying to think of ways to make this more robust, maybe inspect the class path and look for files that match the pattern :thinking: – Jorn Vernee Jun 10 '17 at 16:08
  • 4
    Using the class path probably would be more robust. Using Guava, you could use [`ClassPath`](http://google.github.io/guava/releases/snapshot/api/docs/com/google/common/reflect/ClassPath.html) and do something like `ClassPath.from(Outer.class.getClassLoader()).getAllClasses().stream().filter(info -> info.getName().matches(Outer.class.getName() + "\\$[0-9]+")).map(ClassPath.ClassInfo::load).collect(toList())`. – Radiodef Jun 10 '17 at 16:31
  • 1
    @Radiodef excellent point about potential renaming that could happen. One example is de-sugared lambdas. At the moment it is `lambda$go$0` where `go` is a method where a particular lambda is declared; while it used to be `lambda$0`... – Eugene Jun 10 '17 at 21:06