用java反射实例化私有内部类

Posted

技术标签:

【中文标题】用java反射实例化私有内部类【英文标题】:Instantiate private inner class with java reflection 【发布时间】:2012-12-16 05:40:54 【问题描述】:

是否可以使用 Java 反射从另一个类实例化私有内部类。例如,如果我采用此代码

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


class OtherClass 
    private class Test 

是否可以从 main 类中的 main 方法实例化并获得对 Test 的访问权限。

【问题讨论】:

velocityreviews.com/forums/… 要创建一个嵌套类,你必须有一个外部类的实例。 私有类表明你不应该这样做。 @PeterLawrey 更准确地说,要创建一个 Inner 类(非静态嵌套类),您需要有一个外部类的实例。要创建Static Nested 类的实例,您不需要外部类的实例。 【参考方案1】:

是的,您可以使用 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.***.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

【讨论】:

很抱歉这么晚才回复,但我需要做的是通过获取类的完全限定名来返回类的类对象,而不需要外部类的实例 @popgalop 内部类与方法相同。就像没有对象就不能调用方法一样,没有外部类的对象就不能创建内部类的对象。此规则的唯一例外是嵌套类(就像静态方法一样)。但我想你不能让你的内部类嵌套(通过添加静态修饰符)。 @popgalop 也许如果您告诉我们更多有关您的问题的信息,我们可以找到更好的解决方案。 我正在尝试编写一个方法,该方法将返回一个具有完全限定名称参数的类,并且该方法返回的类可以是任何类(内部,嵌套),不包括匿名内部类 @KasunSiyambalapitiya 我不确定您所说的“创建私有嵌套静态 方法 的实例”是什么意思。实例是类的表示,我们可以在其上调用方法,但方法没有它们的表示(实例)。如果你想问我们是否可以实例化私有静态类,那么可以,只要记住静态内部类与外部类类似,因此它们不需要创建外部实例,因此您可以在构造函数参数中跳过它们。跨度> 【参考方案2】:

使用反射时,您会发现内部类的构造函数将外部类的实例作为附加参数(始终是第一个参数)。

有关相关信息,请参阅这些问题:

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?

示例:

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


【讨论】:

OuterClass.InnerClass.class 只能在 OuterClass 内工作。我们不能在它之外使用InnerClass.class 部分,因为它是私有的,而且很可能我们的代码不会是OuterClass 的一部分(如果它是反射则不需要)。 @Pshemo outer.new InnerClass(); 确实会在这里做。当从外部使用反射访问内部类时,可能希望它实现一个可访问的接口,以便向下转换为可用类型,以避免通过反射调用单个方法的痛苦。但是,如果这一切都是在自己的代码中完成的(而不是针对第三方库等的一些变通办法),那么它可能是一个设计错误 - 所以,根本不理想。与此示例类似,如果受保护成员不是最终的,则可以从同一包或外部类的子类中“窃取”受保护的成员。【参考方案3】:

您可以执行以下操作:

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

【讨论】:

以上是关于用java反射实例化私有内部类的主要内容,如果未能解决你的问题,请参考以下文章

java反射基础知识

java中反射的三种方法是?

java单例之enum实现方式

关于用java反射调用一个类里面的方法并执行

java入门篇9 --- 反射

java基础——反射