Instantiate private inner class with java reflection

popgalop picture popgalop · Jan 1, 2013 · Viewed 42.4k times · Source

Is it possible to instantiate a private inner class from another class using Java reflection. For example if I took this code

public class Main {
    public static void main(String[] args) {}
}

class OtherClass {
    private class Test {}
}

is it possible to instantiate and gain access to Test from the main method in the class main.

Answer

Pshemo picture Pshemo · Jan 1, 2013

Yes, you can instantiate a private inner class with Java reflection. To do that, you need to have an instance of outer class and invoke the inner class constructor which will use outer class instance in its first argument.

class OuterClass {
    private class InnerClass {
        {
            //this block is just to confirm that the inner object was created
            //it will be added to every constructor of this class
            System.out.println("inner object created");
        }
    }
}

When we don't know name of private inner class and we assume that it has no-argument constructor:

class Main {

    //no comment version
    public static Object giveMeInnerInstance() throws Exception{
        OuterClass outerObject = new OuterClass();
        Class<?> innerClass = OuterClass.class.getDeclaredClasses()[0];
        Constructor<?> constructor = innerClass.getDeclaredConstructors()[0];
        constructor.setAccessible(true);
        return constructor.newInstance(outerObject);
    }

    //commented version
    public static void main(String[] args) throws Exception {
        //we need an outer class object to use the inner object constructor
        //(the inner class object needs to know about its parent object)
        OuterClass outerObject = new OuterClass();

        //let's get the inner class 
        //(we know that the outer class has only one inner class, so we can use index 0)
        Class<?> innerClass = OuterClass.class.getDeclaredClasses()[0];
        //or if we know name of inner class we can use 
        //Class<?> innerClass = Class.forName("full.package.name.OuterClass$InnerClass")

        //since constructor so we could use it to pass instance of outer class and change 
        //its accessibility. We can use this code to get default constructor of InnerClass 
        //since we know that this is the only constructor here
        Constructor<?> constructor = innerClass.getDeclaredConstructors()[0];
        //we could also use 
        //Constructor<?> constructor = innerClass.getDeclaredConstructor(OuterClass.class);

        //the default constructor of the private class has same visibility that class has
        //so it is also private, so to be able to use it we need to make it accessible
        constructor.setAccessible(true);

        //now we are ready to create inner class instance
        Object innerObject = constructor.newInstance(outerObject);
    }
}

Now we can make this code clearer if we have informations like

  • name of inner class,
  • constructor arguments

So instead of checking list of inner classes and picking first one, we can get selected inner class by its name using

Class<?> inner = Class.forName("our.pack.age.OuterClass$InnerClass")
//                                                     ^^^^^^^^^^^

Similarly we can select constructor we want to use by invoking getDeclaredConstructor(outerType,rest,of,parameter,types) so if our inner class would look like

class OuterClass {
    private class InnerClass {

        private int x;

        public InnerClass(int x) {
            this.x = x;
            System.out.println("inner object created");
        }

    }
}

our code could be

class ReflectionDemo {

    //no comment version
    public static Object giveMeInnerInstance() throws Exception{
        OuterClass outerObject = new OuterClass();
        Class<?> innerClass = Class.forName("com.stackoverflow.q14112166.OuterClass$InnerClass");
        Constructor<?> constructor = innerClass.getDeclaredConstructor(OuterClass.class, int.class);
        constructor.setAccessible(true);
        return constructor.newInstance(outerObject,42);
    }

    public static Object getFieldValue(Object obj, String fieldName) throws Exception{
        Class<?> clazz = obj.getClass();
        Field field = clazz.getDeclaredField(fieldName);
        field.setAccessible(true);
        return field.get(obj);
    }

    //lets test our code
    public static void main(String[] args) throws Exception {
        Object innerClassObject = giveMeInnerInstance();
        System.out.println(getFieldValue(innerClassObject, "x"));           
    }
}

Output:

inner object created
42