【问题标题】:Is it possible to create an instance of inner class using Java Reflection?是否可以使用 Java 反射创建内部类的实例?
【发布时间】:2010-01-19 23:20:39
【问题描述】:

代码示例:

public class Foo
{
    public class Bar
    {
         public void printMesg(String body)
         {
             System.out.println(body);
         }
    }
    public static void main(String[] args)
    {
         // Creating new instance of 'Bar' using Class.forname - how?
    }        
}

是否可以创建新的类 Bar 实例并给出其名称?我尝试使用:

Class c = Class.forName("Foo$Bar")

它找到了类,但是当我使用 c.newInstance() 时它会抛出 InstantiationException。

【问题讨论】:

  • Nitpick:这不是嵌套类,它是成员内部类。嵌套类是静态的,可以通过您刚刚尝试的机制轻松实例化。
  • InstantiationException 的详细信息是什么?
  • 内部类是一种嵌套类(如果我没记错 JLS 术语的话)。
  • @Tom:口香糖,你是对的......刚刚检查了 JLS (java.sun.com/docs/books/jls/third_edition/html/…)......“内部类是一个嵌套类,它没有显式或隐式声明为静态的”。我的错。

标签: java reflection inner-classes


【解决方案1】:

您需要跳过几圈才能做到这一点。首先,您需要使用Class.getConstructor() 找到您要调用的Constructor 对象:

返回一个构造函数对象 反映指定公众 表示的类的构造函数 通过这个 Class 对象。这 parameterTypes 参数是一个数组 的类对象,标识 构造函数的形参类型, 按照宣布的顺序。 如果这个类 对象代表一个内部类 在非静态上下文中声明, 形式参数类型包括 显式封闭实例作为 第一个参数。

然后你使用Constructor.newInstance():

如果构造函数的声明类 是非静态的内部类 上下文,第一个参数 构造函数必须是封闭的 实例

【讨论】:

  • 这就是我所需要的。感谢您的完整解释!
【解决方案2】:

如果不先构造父类,确实无法构造内部类。它不能存在于父类之外。在进行反射时,您必须传递父类的实例。嵌套类是static,它们可以独立于父类使用,因此在进行反射时也是如此。

这是一个SSCCE,它演示了所有内容。

package mypackage;

import java.lang.reflect.Modifier;

public class Parent {

    public static class Nested {
        public Nested() {
            System.out.println("Nested constructed");
        }
    }

    public class Inner {
        public Inner() {
            System.out.println("Inner constructed");
        }
    }

    public static void main(String... args) throws Exception {
        // Construct nested class the normal way:
        Nested nested = new Nested();

        // Construct inner class the normal way:
        Inner inner = new Parent().new Inner();

        // Construct nested class by reflection:
        Class.forName("mypackage.Parent$Nested").newInstance();

        // Construct inner class by reflection:
        Object parent = Class.forName("mypackage.Parent").newInstance();
        for (Class<?> cls : parent.getClass().getDeclaredClasses()) {
            if (!Modifier.isStatic(cls.getModifiers())) {
                // This is an inner class. Pass the parent class in.
                cls.getDeclaredConstructor(new Class[] { parent.getClass() }).newInstance(new Object[] { parent });
            } else {
                // This is a nested class. You can also use it here as follows:
                cls.getDeclaredConstructor(new Class[] {}).newInstance(new Object[] {});
            }
        }
    }
}

这应该产生

嵌套构造 内部构造 嵌套构造 内部构造 嵌套构造

【讨论】:

  • 优秀而完整的例子!
【解决方案3】:

快速而肮脏的代码:

Foo.Bar.class.getConstructors()[0].newInstance(new Foo());

解释:你必须告诉 Bar 关于它的封闭 Foo。

【讨论】:

  • 这避开了大部分问题,因为 Bar 可能不是静态的和/或甚至可能不可见......
  • 呃,我的回答是假设类不是静态的?如果类是不可见的,OP 会得到 IllegalAccessException,而不是 InstantiationException ...
【解决方案4】:

是的。请记住,您需要将外部实例提供给内部类。使用javap 查找构造函数。你需要通过java.lang.reflect.Constructor 而不是依赖邪恶的Class.newInstance

Compiled from "Foo.java"
public class Foo$Bar extends java.lang.Object{
    final Foo this$0;
    public Foo$Bar(Foo);
    public void printMesg(java.lang.String);
}

javap -c 在构造函数上很有趣,因为(假设 -target 1.4 或更高版本,现在是隐式的)您在调用超级构造函数之前获得了一个实例字段的赋值(过去是非法的)。

public Foo$Bar(Foo);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield        #1; //Field this$0:LFoo;
   5:   aload_0
   6:   invokespecial   #2; //Method java/lang/Object."<init>":()V
   9:   return

【讨论】:

  • 我以前从未听说过 javap。感谢您向我展示了那个不错的工具:)。
【解决方案5】:

其他答案已经解释了你如何去做你想做的事情。

但我想向您建议,您需要这样做的事实表明您的系统设计存在一些问题。我建议您在封闭类上需要一个(非静态)工厂方法,或者您需要将内部类声明为静态。

反射性地创建(非静态)内部类实例具有破坏封装的“气味”。

【讨论】:

    【解决方案6】:

    这不是完全最优的,但它适用于内部类和内部静态类的深度。

    public <T> T instantiateClass( final Class<T> cls ) throws CustomClassLoadException {
        try {
            List<Class<?>> toInstantiate = new ArrayList<Class<?>>();
            Class<?> parent = cls;
            while ( ! Modifier.isStatic( parent.getModifiers() ) && parent.isMemberClass() ) {
                toInstantiate.add( parent );
                parent = parent.getDeclaringClass();
            }
            toInstantiate.add( parent );
            Collections.reverse( toInstantiate );
            List<Object> instantiated = new ArrayList<Object>();
            for ( Class<?> current : toInstantiate ) {
                if ( instantiated.isEmpty() ) {
                    instantiated.add( current.newInstance() );
                } else {
                    Constructor<?> c = current.getConstructor( instantiated.get( instantiated.size() - 1 ).getClass() );
                    instantiated.add( c.newInstance( instantiated.get( instantiated.size() - 1 ) ) );
                }
            }
            return (T) instantiated.get( instantiated.size() - 1 );
        } catch ( InstantiationException e ) {
            throw new CustomClassLoadException( "Failed to load class.", e );
        } catch ( IllegalAccessException e ) {
            throw new CustomClassLoadException( "Failed to load class.", e );
        } catch ( SecurityException e ) {
            throw new CustomClassLoadException( "Failed to load class.", e );
        } catch ( NoSuchMethodException e ) {
            throw new CustomClassLoadException( "Failed to load class.", e );
        } catch ( IllegalArgumentException e ) {
            throw new CustomClassLoadException( "Failed to load class.", e );
        } catch ( InvocationTargetException e ) {
            throw new CustomClassLoadException( "Failed to load class.", e );
        }
    }
    

    【讨论】:

      【解决方案7】:

      这里是嵌套类(静态内部)的答案: 在我的情况下,我需要通过其完全限定名称获取类型

      Class.forName(somePackage.innerClass$outerClass).getConstructor().newInstance();
      

      $”至关重要!

      带有一个点,您将获得类“package.innerClass.outerClass”的 ClassNotFoundException。例外是误导性的:-(。

      【讨论】:

        猜你喜欢
        • 2012-12-16
        • 2014-06-14
        • 2011-05-07
        • 2013-07-03
        • 2015-03-01
        • 2015-11-02
        • 1970-01-01
        • 2011-10-11
        • 2011-09-12
        相关资源
        最近更新 更多