Java constructor is not so intuitive. Or perhaps it's not Java, it's C# that is not intuitive

StackOverflow https://stackoverflow.com/questions/10611726

  •  09-06-2021
  •  | 
  •  

質問

Given this Java code, this outputs 0 and 4:

class A{
   A() {  print();   }
   void print() { System.out.println("A"); }
}

class B extends A{
   int i =   Math.round(3.5f); 

   public static void main(String[] args){
      A a = new B();
      a.print();
   }
   void print() { System.out.println(i); }
}

And with this identical C# code, this outputs 4 and 4

using System;

class A{
   internal A() {  print();   }
   virtual internal void print() { Console.WriteLine("A"); }
}

class B : A{
   int i =  (int) Math.Round(3.5f); 

   public static void Main(string[] args){
      A a = new B();
      a.print();
   }
   override internal void print() { Console.WriteLine(i); }
}

Though I figure out that the output should be 4 and 4 on Java, but the answer is actually 0 and 4 on Java. Then I tried it in C#, the answer is 4 and 4

What gives? Java rationale is, during construction of B, A is still initializing(consequently I posit B is still initializing if Java said A is still initializing), so the default value should be 0. Hence the output is 0 and 4 in Java.

Why C# constructor behavior differs from Java, or vice-versa?

役に立ちましたか?

解決

It's happening because of differences in the ordering of object initialisation in constructors.

What is happening in Java:

  • (empty, implicit) Constructor of B is called
  • Superclass Construtor of A is called (prints 0 since i is uninitialised)
  • i is initialised after the superclass constructor
  • print() is called (prints 4)

What is happening in C#:

  • (empty, implicit) Constructor of B is called
  • i is initialised before calling the superclass constructor
  • Superclass Construtor of A is called (prints 4 since i is already initialised)
  • print() is called (prints 4)

Neither is right or wrong - it's just a difference in how the compiler orders the construction operations. Personally I think the Java ordering is a marginally more logical, because it makes sense to me that the superclass is fully constructed before the subclass initialisation takes place.

Either way, because the logic can get quite complicated, I'd suggest that you avoid calling virtual methods during object construction in general.

他のヒント

The order of initialization in Java:

1.The storage of the instance is wiped to zero, automatically setting all the primitives in the object to their default values (zero for numbers and the equivalent for boolean and char) and the references to null.

2.The constructor of the base class A is called. It will the call the print method in class B, since it is an overridden method. i is 0 at this moment.

3.The member initialization of the class B is executed. So i is 4 now.

In order not to produce this kind of surprise, do not call any non-static or non-private methods in a constructor,since they may be overridden in derived classes.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top