java - 使用反射实例化内部类时出现 InstantiationException。为什么?

标签 java generics reflection factory inner-classes

我无法创建 B 对象,但为什么?

public class AFactory {

    public int currentRange;

    private abstract class A {
        protected final Object range = currentRange;

        public int congreteRange = 28;
    }

    public class B extends A {
        public int congreteRange = 42;
    }

    synchronized A createNew(Class<? extends A> clazz) throws Exception {
        // EDIT: there is accessible default constructor
        currentRange = clazz.newInstance().congreteRange;
        return clazz.newInstance();
    }

    public static void main(String[] args) throws Exception {
        AFactory factory = new AFactory();
        System.out.println(factory.createNew(B.class).range);
    }
}

异常(exception)是:

Exception in thread "main" java.lang.InstantiationException: AFactory$B
at java.lang.Class.newInstance0(Class.java:357)
at java.lang.Class.newInstance(Class.java:325)
at AFactory.createNew(AFactory.java:15)
at AFactory.main(AFactory.java:21)

最佳答案

问题是您正在尝试实例化一个内部类,您只能在外部类的实例上访问它。内部类的构造函数采用封闭类的隐式隐藏实例。分析这个简单类的字节码就可以看出:

public class Demo {
    class Test {
    }
}

现在,编译代码:

javac Demo.java

这将创建两个类文件:

Demo.class
Demo$Test.class

运行以下命令查看Demo$Test.class的字节码:

javap -c . Demo$Test

你会得到如下结果:

class Demo$Test {
  final Demo this$0;   

  Demo$Test(Demo);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #1                  // Field this$0:LDemo;
       5: aload_0
       6: invokespecial #2                  // Method java/lang/Object."<init>":
()V
       9: return
}

那么,你看到类的构造函数了吗?它以 Demo 作为参数。因此,没有 0-arg 构造函数。

但是,如果您将内部类设置为static,它就会起作用,因为这样您就不需要任何封闭类的实例来调用内部类构造函数。

使用 static 内部类 - 备选方案:

public class AFactory {

    public static int currentRange;

    private static abstract class A {
        protected final Object range = AFactory.currentRange;
    }

    public static class B extends A {
        public int congreteRange = 42;
    }

    synchronized A createNew(Class<? extends B> clazz) throws Exception {
        currentRange = clazz.newInstance().congreteRange;
        return clazz.newInstance();
    }

    public static void main(String[] args) throws Exception {
        AFactory factory = new AFactory();
        System.out.println(factory.createNew(B.class).range);
    }
}

使用非static 内部类 - 最终代码:

如果您不想让它们成为static,那么您必须首先创建一个封闭类的实例。并将其传递给内部类的构造函数。要获取内部类的构造函数,可以使用Class#getDeclaredConstructor方法。

现在,您必须修改工厂方法以将 Constructor 作为参数。像这样修改您的代码:

public class AFactory {

    public int currentRange;

    private abstract class A {
        protected final Object range = currentRange;
    }

    public class B extends A {
        public int congreteRange = 42;
    }

    synchronized A createNew(Constructor<? extends A> ctor) throws Exception {
        // Pass `this` as argument to constructor. 
        // `this` is reference to current enclosing instance
        return ctor.newInstance(this); 
    }

    public static void main(String[] args) throws Exception {
        AFactory factory = new AFactory();

        // Get constructor of the class with `AFactory` as parameter
        Class<B> bClazz = B.class;
        Constructor<B> ctor = bClazz.getDeclaredConstructor(AFactory.class);

        System.out.println(factory.createNew(ctor));
    }
}

关于java - 使用反射实例化内部类时出现 InstantiationException。为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19137147/

相关文章:

java - JTable 单元格中功能齐全的 JTree 不起作用

java - MyBatis - 如何在<foreach>下写<if>语句?

generics - 在 Haxe 中,您可以编写一个通用接口(interface),其中方法类型参数受类的类型参数约束吗?

java - 在 Java 中使用泛型时出现空指针异常

java - 在反射中处理 HashMap

java - 如何在 JList 中将项目设置为选中

generics - 如何避免传递类型依赖?

c# - 如何通过使用反射设置双泛型来执行泛型方法

c++ - C++ 的属性和反射库?

java - 使用 Java 将 AWS 文件上传到 S3