在构造函数中,为什么可以执行超类构造函数之前未引用的当前类的字段?

时间:2017-02-19 09:06:37

标签: java class oop inheritance

例如,

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

这引入了编译错误“在调用构造函数时无法引用实例字段”。

StackOverflow中关于此场景的类似问题的一些答案转到“因为当前实例仍在构建中”或“尚未在堆中创建的实例”。

然而,令人困惑的是, 在超类构造函数中,可以调用可重写的成员方法,这些方法可以访问子类的当前实例的字段。它在Java中没问题。

我的查询是

  1. 在实例构造的过程中,哪个时刻可以引用子类的字段?

  2. “在调用构造函数时不能引用实例字段”背后的基本原理是什么,而在超类构造函数中调用可重写的方法是可以的?

1 个答案:

答案 0 :(得分:3)

  

在实例构造的过程中,从哪个时刻可以引用子类的字段?

从完成对超类构造函数的调用。请记住,在此之前,实例字段无论如何都只有默认值 - 即使您在它们上使用了初始化程序,或实例初始化块;在这两种情况下,执行该操作的代码都插入到类的构造函数中,超类构造函数调用之后。考虑这对类:

class ParentClass {
    ParentClass(String s) {
    }
}

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

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

如果我们通过Subclass查看javap -c Subclass的字节码,我们会看到:

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
}

请注意,我们编写了我们的代码,好像我们这样写了:

class Subclass extends ParentClass {
    private String subclassField;

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

(是的,如果你很好奇,如果有多个构建函数,那么代码重复。)

  

在调用构造函数时“无法引用实例字段”背后的基本原理是什么,而在超类构造函数中调用可覆盖的方法是否合适?

你必须问詹姆斯戈斯林。但是考虑到与实例字段不同,实例方法在超类构造函数调用之前具有有用的值,实际上在构造期间使用方法有时非常有用。但是,使用可重写的方法是不好的做法;您在施工期间(直接或间接)使用的任何方法应该是最终的或私人的(例如,实际上是最终的)。编译器不会为您做出这种区分;也许如果现在正在编写规则,那就可以了。