Instantiate private inner class with java reflection

后端 未结 3 582
忘了有多久
忘了有多久 2020-11-29 04:09

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         


        
相关标签:
3条回答
  • 2020-11-29 04:20

    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
    
    0 讨论(0)
  • 2020-11-29 04:25

    When using reflection, you'll find constructors of that inner class taking an instance of the outer class as an additional argument (always the first) .

    See these questions for related information:

    • Instantiating inner class

    • How can I instantiate a member class through reflection on Android

    • In Java, how do I access the outer class when I'm not in the inner class?

    Example:

    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    
    public class OuterClass {
    
        private class InnerClass {
    
        }
    
        public OuterClass() {
            super();
        }
    
        public static void main(String[] args) {
            // instantiate outer class
            OuterClass outer = new OuterClass();
    
            // List all available constructors.
            // We must use the method getDeclaredConstructors() instead
            // of getConstructors() to get also private constructors.
            for (Constructor<?> ctor : OuterClass.InnerClass.class
                    .getDeclaredConstructors()) {
                System.out.println(ctor);
            }
    
            try {
                // Try to get the constructor with the expected signature.
                Constructor<InnerClass> ctor = OuterClass.InnerClass.class
                        .getDeclaredConstructor(OuterClass.class);
                // This forces the security manager to allow a call
                ctor.setAccessible(true);
    
                // the call
                try {
                    OuterClass.InnerClass inner = ctor.newInstance(outer);
                    System.out.println(inner);
                } catch (InstantiationException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            } catch (NoSuchMethodException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
    }
    
    0 讨论(0)
  • 2020-11-29 04:47

    You can do the following :

    public static void main(String[] args) throws Exception {
        // to get first class in OtherClass
        Class<?> innerClass = OtherClass.class.getDeclaredClasses()[0];
        // getDeclaredConstructors for private constructor
        Constructor<?> constructor = innerClass.getDeclaredConstructors()[0];
        // to enable accessing private constructor
        constructor.setAccessible(true);
        OtherClass outerObject = new OtherClass();
        //// create instance of Test by reflection
        Object o = constructor.newInstance(outerObject);
        System.out.println(o);
    }
    
    0 讨论(0)
提交回复
热议问题