Reputation: 1986
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
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
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
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