Why child private field is null when printing it from parent class using polymorphism in java?

一笑奈何 提交于 2021-02-17 06:09:22

问题


As far as In understand he getS method from child class BB overrides the same method from the parent class AA. Yet although both classes have initialized the field s which is returned by getS it's printed as null. Why is this happening?

This is the code:

public class AA {
    public String getS() {
        return s;
    }
    private String s = "hello1";
    public AA() {
        System.out.println(service() + getS());
    }
    public static String service() {
        return "A service ";
    }
}

public class BB extends AA {
    private String s = "hello2";
    @Override
    public String getS() {
        return s;
    }
    public static String service() {
        return "B service ";
    }
}

public class CC {
    public static void main(String[] args) {
        BB b =new BB(); //prints "A service null"
    }
}

回答1:


When we call new SomeClass()

  1. new operator first creates object of SomeClass but that object has all its fields set to default values (0, '\0', false, null).

  2. After object is created code of constructor is executed to initialize object properly (set its fields to proper values, and possibly do few other things).

But if class has parent class, constructor first (implicitly or explicitly) calls its super() constructor to ensure that all inherited fields are properly initialized, before we start using inherited methods which may depend on those fields state.

But methods are polymorphic (as long as they are not private, static or final). This means that when in superclass we call method which was overridden, "newest version" of code will be executed (because polymorphism uses actual type of this instance - returned by new keyword - to locate class from which it should start searching for code which should be executed - this is called late or dynamic binding).

So since you called getS() method in AA superclass but on BB instance (because that is what new BB created), overridden code from BB class was executed. Problem is that this code uses s declared in BB class, but that s wasn't initialized yet. Lets see how default constructor of BB which was invoked by new BB() looks like:

BB(){
    super(); //calls AA()
    s = "hello2"; // by default `s` holds `null` value 
                  // all initialization is moved to constructors 
                  // after superconstructor call
}

So

  • before s = "hello2"; is executed s (of BB class) holds null
  • but before s is initialized to "hello2" super() is called (which internally calls getS())
  • since getS() is polymorphic code from BB class for that method will be executed
  • but that code (from BB) uses s of BB which wasn't yet initialized, so it holds null and that is what you see in console.

Because of that it is considered bad practice (in most cases) to put in constructor methods which can be overridden. We should limit ourselves to calling methods which are not polymorphic, which means either private static or final.




回答2:


Here you can see what is the order of initialization when you create an object. That answer is based on Java Specs

Let me copy-paste

Object Initialization

  1. An object is initialized whenever a new object is created, typically by evaluation of a class instance creation expression. This proceeds as follows:

  2. Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.

  3. If this constructor begins with an explicit constructor invocation (§8.8.7.1) of another constructor in the same class (using this), then evaluate the arguments and process that constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason; otherwise, continue with step 5.

  4. This constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this). If this constructor is for a class other than Object, then this constructor will begin with an explicit or implicit invocation of a superclass constructor (using super). Evaluate the arguments and process that superclass constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue with step 4.

  5. Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception. Otherwise, continue with step 5.

  6. Execute the rest of the body of this constructor. If that execution completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, this procedure completes normally.

When new BB() is executed, the default constructor is invoked

 public BB() {
      super();
 }

super() is stated in your code as

public AA() {
    System.out.println(service() + getS());
}

service() is an static method and it can not be overriden, so A.service() method is invoked and its result is concatenated with the result of invoking the overrided method B.getS()

At this point (see 5), the member B.s has not been initialized yet, its value is null.

And these are the reasons of obtaining

A service null



回答3:


It happens because of the class hierarchy. When run the

 System.out.println(service() + getS());

It calls the overridden getS() method. In that it prints attribute s which declare in the class BB.(It not access s attribute in class AA because it private).

At that point(When running the constructor in class AA) attributes in class BB are not initialized. Because of that it prints null.




回答4:


The reason is BB doesn't have the constructor itself. new BB() calls to public AA() then the statement below will be execute:

System.out.println(service() + getS());

then it call inside A, so:

  • service: method of A

  • getS(): return default value of s (null),

calling constructor means the object wasn't created yet, then the initialize of properties (s) still null.




回答5:


the other answers have pointed out the problem.

what you can is to use an instance initialiser. since it is called before the constructor body. an instance initialiser is defined as a {} block inside the class.

public class AA {
    private String s ;       

    { //this is an instance initialiser. 
      //it is called before the constructor body

         s = "hello1";
    }
    public AA() {
        //the constructor body is called AFTER the instance initialiser
        System.out.println(service() + getS());
    }
    public String getS() {
        return s;
    }
    public static String service() {
        return "A service ";
    }
}


来源:https://stackoverflow.com/questions/48922077/why-child-private-field-is-null-when-printing-it-from-parent-class-using-polymor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!