web-dev-qa-db-ja.com

Javaリフレクションでプライベート内部クラスをインスタンス化する

Javaリフレクションを使用して、別のクラスからプライベート内部クラスをインスタンス化することは可能ですか。たとえば、このコードを使用した場合

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

class OtherClass {
    private class Test {}
}

クラスmainのmainメソッドからTestをインスタンス化し、アクセスすることは可能ですか?.

32
popgalop

リフレクションを使用する場合、外部クラスのインスタンスを追加の引数(常に最初の引数)として使用する、内部クラスのコンストラクターが見つかります。

関連情報については、次の質問を参照してください。

例:

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();
        }
    }

}
16
Sam

はい、プライベートな内部クラスをJavaリフレクションでインスタンス化できます。これを行うには、外部クラスのインスタンスを用意し、最初に外部クラスインスタンスを使用する内部クラスコンストラクターを呼び出す必要があります引数。

_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");
        }
    }
}
_

プライベート内部クラスの名前がわからず、引数のないコンストラクタがあると仮定した場合:

_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);
    }
}
_

次のような情報がある場合、このコードをより明確にすることができます

  • 内部クラスの名前、
  • コンストラクター引数

したがって、内部クラスのリストをチェックして最初のリストを選択する代わりに、名前を使用して選択した内部クラスを取得できます

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

同様に、getDeclaredConstructor(outerType,rest,of,parameter,types)を呼び出すことで、使用するコンストラクターを選択できます。そのため、内部クラスが次のようになります。

_class OuterClass {
    private class InnerClass {

        private int x;

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

    }
}
_

私たちのコードは

_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"));           
    }
}
_

出力:

_inner object created
42
_
29
Pshemo