34

代码示例:

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。

4

7 回答 7

54

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

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

然后你使用Constructor.newInstance()

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

于 2010-01-19T23:25:59.787 回答
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[] {});
            }
        }
    }
}

这应该产生

嵌套构造
内部构造
嵌套构造
内部构造
嵌套构造
于 2010-01-19T23:47:54.913 回答
7

快速而肮脏的代码:

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

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

于 2010-01-19T23:29:16.397 回答
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
于 2010-01-19T23:24:19.133 回答
1

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

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

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

于 2010-01-20T01:28:04.627 回答
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 );
    }
}
于 2013-11-01T17:03:28.427 回答
-1

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

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

' $ ' 很关键!

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

于 2013-05-14T10:42:43.790 回答