スーパータイプコンストラクターが呼び出される前に「X」を参照できない

1. 概要

この短いチュートリアルでは、スーパータイプコンストラクターが呼び出される前に_Cannot reference“ X”エラーを取得する方法と、それを回避する方法を示します。

2. コンストラクターチェーン

コンストラクターは、他のコンストラクターを1つだけ呼び出すことができます。 この呼び出しは、本体の最初の行になければなりません。
キーワード_this_を使用して同じクラスのコンストラクターを呼び出すことも、キーワード_super_を使用してスーパークラスのコンストラクターを呼び出すこともできます。
*コンストラクターが別のコンストラクターを呼び出さない場合、コンパイラーはスーパークラスの引数なしコンストラクターへの呼び出しを追加します。*

3. コンパイルエラー

このエラーは、コンストラクターチェーンを呼び出す前に、*インスタンスレベルのメンバーにアクセスしようとしています。*
これに遭遇する可能性のあるいくつかの方法を見てみましょう。

3.1. インスタンスメソッドの参照

次の例では、5行目でスーパータイプコンストラクターが呼び出される前に、コンパイルエラー_ * Cannot reference“ X”が表示されます* _。 コンストラクターは、インスタンスメソッド_getErrorCode()_を早めに使用しようとすることに注意してください。
public class MyException extends RuntimeException {
    private int errorCode = 0;

    public MyException(String message) {
        super(message + getErrorCode()); // compilation error
    }

    public int getErrorCode() {
        return errorCode;
    }
}
このエラーは、* _ super()_が完了するまで*、クラス_MyException_のインスタンスがないためです。 したがって、インスタンスメソッド_getErrorCode()_を呼び出すことはまだできません。

3.2. インスタンスフィールドの参照

次の例では、インスタンスメソッドの代わりにインスタンスフィールドを持つ例外が表示されます。 インスタンス自体の準備ができる前に、最初のコンストラクターがインスタンスメンバーを使用しようとする方法を見てみましょう。*
public class MyClass {

    private int myField1 = 10;
    private int myField2;

    public MyClass() {
        this(myField1); // compilation error
    }

    public MyClass(int i) {
        myField2 = i;
    }
}
インスタンスフィールドへの参照は、そのクラスが初期化された後、つまり_this()_または_super()_を呼び出した後にのみ作成できます。
では、なぜインスタンスフィールドも使用する2番目のコンストラクターにコンパイラエラーがないのですか?
*すべてのクラスはクラス_Object_ *から暗黙的に派生しているため、コンパイラによって暗黙的な__super()__callが追加されることに注意してください。
public MyClass(int i) {
    super(); // added by compiler
    myField2 = i;
}
ここでは、_myField2_にアクセスする前に_Object_ ’のコンストラクターが呼び出されます。つまり、大丈夫です。

4. ソリューション

この問題の最初の可能な解決策は簡単です:* 2番目のコンストラクターを呼び出さない。* 2番目のコンストラクターでやりたいことを最初のコンストラクターで明示的に行う。
この場合、_myField1_の値を_myField2_にコピーします。
public class MyClass {

    private int myField1 = 10;
    private int myField2;

    public MyClass() {
        myField2 = myField1;
    }

    public MyClass(int i) {
        myField2 = i;
    }
}
ただし、一般的には、*私たちはおそらく、構築しているものの構造を再考する必要があるでしょう。*
ただし、コードの繰り返しを避けるためなど、正当な理由で2番目のコンストラクターを呼び出す場合は、*コードをメソッドに移動できます:*
public class MyClass {

    private int myField1 = 10;
    private int myField2;

    public MyClass() {
        setupMyFields(myField1);
    }

    public MyClass(int i) {
        setupMyFields(i);
    }

    private void setupMyFields(int i) {
        myField2 = i;
    }
}
繰り返しますが、これは、コンパイラがメソッドを呼び出す前にコンストラクタチェーンを暗黙的に呼び出しているために機能します。
3番目の解決策は、* staticフィールドまたはメソッド*を使用することです。 _myField1_を静的定数に変更すると、コンパイラーも満足します。
public class MyClass {

    private static final int SOME_CONSTANT = 10;
    private int myField2;

    public MyClass() {
        this(SOME_CONSTANT);
    }

    public MyClass(int i) {
        myField2 = i;
    }
}
フィールドを_static_にすることは、このオブジェクトのすべてのインスタンスと共有されることを意味することに注意してください。したがって、軽すぎる変更はありません。
_static_が正しい答えであるためには、強力な理由が必要です。 たとえば、値は実際にはフィールドではなく定数である可能性があるため、_static_および_final_にするのが理にかなっています。 おそらく、呼び出したい構築メソッドは、クラスのインスタンスメンバーにアクセスする必要がないので、_static_である必要があります。

5. 結論

この記事では、_super()_または_this()_呼び出しの前にインスタンスメンバーへの参照を作成すると、コンパイルエラーが発生する様子を見ました。 これは、明示的に宣言された基本クラスと、暗黙の_Object_基本クラスでも発生します。
また、これはコンストラクターの設計の問題であることを実証し、コンストラクターでコードを繰り返す、構築後のセットアップメソッドに委任する、または構築に役立つ定数値または静的メソッドを使用することでこれを修正する方法を示しました。
いつものように、この例のソースコードはhttps://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-lang-oop-2[GitHubで]にあります。