当使用反射实例化内部类时,InstantiationException.为什么? [英] InstantiationException while instantiating inner class using reflection. Why?

查看:137
本文介绍了当使用反射实例化内部类时,InstantiationException.为什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

i cant create B-Object, but why?

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

推荐答案

问题是您试图实例化内部类,您只能在外部类的实例上进行访问.内部类的构造函数采用封闭类的隐式隐藏instance.您可以通过分析此简单类的字节码来查看它:

The issue is you are trying to instantiate an inner class, which you can only access on an instance of the outer class. The constructor of the inner classes takes an implicit hidden instance of the enclosing class. You can see it by analyzing the byte code of this simple class:

public class Demo {
    class Test {
    }
}

现在,编译代码:

javac Demo.java

这将创建两个类文件:

Demo.class
Demo$Test.class

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

Run the following command to see the byte code of 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构造函数.

So, you see the constructor for the class? It takes Demo as parameter. So, there is no 0-arg constructor.

但是,如果您将内部类设置为static,则它将起作用,因为那样一来,您就不需要任何封装类实例来调用内部类构造函数.

However, if you make your inner classes static, it would work, because then you don't need any instance of enclosing class to invoke inner class constructor.

具有static内部类-替代:

With static inner classes - Alternative:

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内部类-最终代码:


With non-static inner classes - Final Code:

如果不想将它们设置为static,则必须首先创建封闭类的实例.并将其传递给内部类的构造函数.要获取内部类的构造函数,可以使用

If you don't want to make them static, then you would have to first create an instance of the enclosing class. And pass it to the constructor of the inner class. To get the constructor of the inner class, you can use Class#getDeclaredConstructor method.

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

Now, you would have to modify your factory method to take a Constructor as parameter. Modify your code like this:

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

这篇关于当使用反射实例化内部类时,InstantiationException.为什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆