2

for example,

Subclass extends ParentClass {
  private String subclassField;
  Subclass() {
    // it's illegal
    super(subclassField);
  }
}

This introduces compile error "cannot refer to an instance field while invoking a constructor".

Some answers to similar questions about this scenario in StackOverflow go to "Because the current instance is still under construction" or "The instance not yet created in the heap".

However, what is confusing is that, In superclass constructor, overriddable member methods can be invoked, which may access fields of current instance of the subclass. and it's okay in Java.

My query is

  1. Since which moment can the fields of subclass be referred to, in the process of the instance construction?

  2. What is the rationale behind "cannot refer to an instance field while invoking a constructor", whereas invoking overriddable method in superclass constructor is okay?

Arjun
  • 144
  • 2
  • 14
dannail
  • 455
  • 2
  • 10
  • 27
  • 1
    What is the *practical* problem that you're asking about with #2? E.g., why does the *rationale* matter? That's just the way the language is. – T.J. Crowder Feb 19 '17 at 09:10
  • 2
    *and it's okay in Java*: it's accepted by the compiler, but is bad practice. All the fields would have their default value anyway, so you can just pass null instead of subclassField. Passing a subclass field has 99.999% chance of being a bug, so disabling it makes sense. Calling an overridable method *can* be fine, because it doesn't necessarily access subclass fields. – JB Nizet Feb 19 '17 at 09:11

1 Answers1

3

Since which moment can the fields of subclass be referred to, in the process of the instance construction?

From the point where the call to the superclass constructor is complete. Remember that prior to that time, instance fields will only have their default values anyway — even if you've used an initializer on them, or an instance initialization block; in both cases, the code to do that is inserted in the constructor(s) for the class, after the superclass constructor call. Consider this pair of classes:

class ParentClass {
    ParentClass(String s) {
    }
}

class Subclass extends ParentClass {
    private String subclassField = "init";

    Subclass() {
        super("bar");
    }
}

if we look at the bytecode of Subclass via javap -c Subclass, we see:

class Subclass extends ParentClass {
  Subclass();
    Code:
       0: aload_0
       1: ldc           #1                  // String bar
       3: invokespecial #2                  // Method ParentClass."":(Ljava/lang/String;)V
       6: aload_0
       7: ldc           #3                  // String init
       9: putfield      #4                  // Field subclassField:Ljava/lang/String;
      12: return
}

Note that that's compiled our code as though we'd written it like this:

class Subclass extends ParentClass {
    private String subclassField;

    Subclass() {
        super("bar");
        this.subclassField = "init"; // *** Note this moved
    }
}

(And yes, if you're curious, that code is repeated in every constructor if there's more than one.)

What is the rationale behind "cannot refer to an instance field while invoking a constructor", whereas invoking overriddable method in superclass constructor is okay?

You'd have to ask James Gosling. But consider that unlike instance fields, instance methods have useful values prior to the superclass constructor call, and in fact using a method during construction can be very useful at times. Using an overrideable method is poor practice, however; any methods you use during construction (directly or indirectly) should be either final or private (e.g., effectively final). The compiler doesn't make that distinction for you; perhaps if the rules were being written now, it would.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875