Grim
Grim

Reputation: 1986

InstantiationException while instantiating inner class using reflection. Why?

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 is:

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)

Upvotes: 3

Views: 2769

Answers (3)

Rohit Jain
Rohit Jain

Reputation: 213243

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 {
    }
}

Now, compile the code:

javac Demo.java

This will create two class files:

Demo.class
Demo$Test.class

Run the following command to see the byte code of Demo$Test.class:

javap -c . Demo$Test

You will get following result:

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
}

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

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.

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

With non-static inner classes - Final Code:

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.

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

Upvotes: 2

Aniket Thakur
Aniket Thakur

Reputation: 68935

Change your classes and variables to static.

public class AFactory {

public static int currentRange;

private static abstract class A {
    protected final Object range = 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);
}
}

and output is 42.

Upvotes: -1

Avi
Avi

Reputation: 21858

You can't create A because you defined clazz to be of type that extends B and A doesn't extend B - it's the other way around (B extends A).

You should change the createNew signature to be:

synchronized A createNew(Class<? extends A> clazz) throws Exception

Upvotes: 1

Related Questions