简体   繁体   中英

Can we get local classes using reflection?

Is there a way to get Class X from Test.class

class Test {

    void test() {
        class X {
        }
    }
}

using reflection?

It's similar to how you'd do an inner class using Class.forName

class Test {
    class X {} // "Test$X"
}

Except it's prefaced by a number (much like an anonymous inner class) since there can be multiple local definitions

class Test {
    void test() {
        class X { // "Test$1X"
        }
    }
    void test2() {
        class X { // "Test$2X"
        }
        class Y { // "Test$1Y"
        }
        System.out.println(new X(){}) // "Test$1"
        System.out.println(new X(){}) // "Test$2"
        System.out.println(new Y(){}) // "Test$3"
    }
}

It would be interesting to know if the JLS guarantees the numbering based on the order of the code, but I wouldn't place any bets on it.

You can use the following idiom, assuming a self-contained example in a Main class:

public class Main {

    public static void main(String[] args) throws Exception {
        Class c = Class.forName("test.Main$Test$1Foo");
        // testing methods
        System.out.println(Arrays.toString(c.getDeclaredMethods()));
    }
    static class Test {
        void test() {
            class Foo {
                void bar(){}
            }
        }
    }
}

Output

[void test.Main$Test$1Foo.bar()]

You can identify these anonymous classes using reflection, by accessing the classes field on the ClassLoader object. This field appears to keep track of all the loaded classes. If you then check for classes where getCanonicalName return null, you will then be able to identify these classes. It is not very pretty, but it gets me class X in this case.

The only caveat is that this doesn't appear to work if class X is not referenced anywhere.

public class Main {
  public static void main(String... args) throws NoSuchFieldException, IllegalAccessException {
    class X {

    }

    ClassLoader classLoader = RiskWrapper.class.getClassLoader();

    System.setSecurityManager(new SecurityManager() {
      @Override
      public void checkPermission(Permission perm) {

      }
    });
    Field classes = ClassLoader.class.getDeclaredField("classes");
    classes.setAccessible(true);
    Vector<Class<?>> loadedClasses = (Vector<Class<?>>)classes.get(classLoader);

    Class<X> xClass = X.class;

    for(Class<?> loadedClass : loadedClasses) {
      if(loadedClass.equals(xClass)) {
        System.out.println("Found anonymous class " + loadedClass.getName());

      }

    }

    System.out.println();

  }
}

Output:

Found anonymous class Main$1X

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM