Java Abstract class Unusual behavior
Abstract Class:
public abstract class ParentClass {
private static ParentClass mpParentClass;
public ParentClass() {
mpParentClass = this;
}
public abstract void method1();
public static ParentClass getInstance() {
return mpParentClass;
}
}
Child Class :
public class ChildClass extends ParentClass{
@Override
public void method1() {
System.out.print("ChildClass class method");
}
}
Test Class :
public class TestClass {
public static void main(String[] args) {
ChildClass cl = new ChildClass();
ParentClass.getInstance().method1();
}
}
Here I have created an abstract class and a Child class which extends the parent Abstract class.
Parent abstract class holds a reference to its own instance and returns the instance through a static method.
In Test class, if I don't create an object of ChildClass, java throws NullPointerException.
But after creating object of ChildClass, and then querying instance of ParentClass and invoking abstract method, it calls method implemented by ChildClass.
I am unable to understand this behavior . Please anyone explain.
The first time you Instantiate a ChildClass you use the default constructor of parentClass which instantiate the private field with the ChildClass type . If you don't do that, the private field mpParentClass is not instantiate. So you have a NullPointerException
ParentClass.getInstance()
is a static method so it doesn't require an instance of your class to run.
By calling this method you will return the static member mpParentClass
. But by default this member contains a null
reference.
So without doing anything this will indeed result in a NullPointerException
because you didn't call the constructor of ParentClass
.
In your example you first make an instance of the ChildClass
.
This will call the default constructor of that class. This default constructor has the standard behavior of calling the default constructor of the super class (by calling super()
).
So by instantiating the ChildClass
you call the constructor of ParentClass
that will set the mpParentClass
datamember to this. And here this refers to the instance of the ChildClass
you are creating.
So after construction mpParentClass
will contain the newly created instance of the ChildClass
.
Here's what's happening.
When you call the constructor for your ChildClass
, it's implicit that the first actual call in that method is to the superclass constructor. If you had a superclass constructor that required/allowed alternate arguments, you could call it manually. But it's happening for you.
When the superclass constructor is called, a static
reference is being assigned to that new instance, which is a ChildClass
instance. (Because that's what this
is, in this case.)
If you were to call:
new ChildClass();
new ParentClass() {
public void method1() {
System.out.println("Anonymous class!");
}
};
ParentClass.getInstance().method1();
You would see "Anonymous class!"
, because there is one static reference that is going to be reassigned every time you create any instance of a ParentClass
implementation.
Regarding your NullPointerException
- the only place where mpParentClass
is assigned to a value is in the constructor for ParentClass
. If you never create an instance of a ParentClass
, then this code will never be called, and mpParentClass
will be left with its original value, which is null
. Attempting to call a method or access a property on a null
reference is what produces a NullPointerException
.
The question to ask is: If you never instantiate any of your implementations (by 'calling' their constructors), what do you expect the mpParentClass
variable to be set to, if not null
?
上一篇: 抽象工厂设计:用枚举代替
下一篇: Java抽象类不寻常的行为