是否可以使用Java Reflection创建嵌套类的实例?

时间:2010-01-19 23:20:39

标签: java reflection inner-classes

代码示例:

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。

7 个答案:

答案 0 :(得分:54)

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

  

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

然后你使用Constructor.newInstance()

  

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

答案 1 :(得分:25)

如果不首先构造父类,确实不能构造内部类。它不能存在于父类之外。在进行反射时,您必须传递父类的实例。嵌套类是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[] {});
            }
        }
    }
}

这应该产生

Nested constructed
Inner constructed
Nested constructed
Inner constructed
Nested constructed

答案 2 :(得分:7)

快速而肮脏的代码:

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

说明:您必须告诉Bar其封闭的Foo。

答案 3 :(得分:2)

是。请记住,您需要将外部实例提供给内部类。使用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

答案 4 :(得分:1)

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

但是我想告诉你,你需要这样做的事实表明你的系统设计有点不对劲。我建议您在封闭类上需要(非静态)工厂方法,或者需要将内部类声明为静态。

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

答案 5 :(得分:0)

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

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

答案 6 :(得分:-1)

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

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

' $ '至关重要!

使用点,您将获得类“package.innerClass.outerClass”的ClassNotFoundException。例外是误导: - (。

相关问题