简体   繁体   中英

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 ?

Parent class is abstract so you can't use the constructor directly. The static variable instance is null by default which leads to your NullPointerException. The only way to set this variable is by calling the constructor which is only called when a constructor of a child class is called.

This is because you are only assigning to the static field mpParentClass in your ParentClass constructor. Therefore, until you create an instance of ChildClass , mpParentClass is null. In your example, the base class ParentClass constructor is called implicitly when you create an instance of the derived class ChildClass .

Might I suggest that you use the singleton pattern instead?

public class Singleton {
  // Private constructor prevents instantiation from other classes
  private Singleton() {}

 /**
   * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
   * or the first access to SingletonHolder.INSTANCE, not before.
   */
  private static class SingletonHolder { 
    private static final Singleton INSTANCE = new Singleton();
  }

  public static Singleton getInstance() {
    return SingletonHolder.INSTANCE;
  }
}

You're creating a ChildClass then calling its method1() through unnecessary trickery.

You do realize that this will be a ChildClass instance at the ParentClass constructor, right? It has to be, since the parent class is abstract and therefore there can never be a this instance that would represent it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM