S-JIS[2025-09-21] 変更履歴

this()・super()の前に文を書く

Javaでthis()・super()の前に文を書けるようにする緩和について。


概要

Java25(プレビュー版ではJava22〜24)で、this()やsuper()の前に文が書けるようになった。

親コンストラクター呼び出し(super())や代替コンストラクター呼び出し(this())の前に文を書くことが出来る。

ただし、そこでは、初期化されていない領域にはアクセスできない。(コンパイルエラーになる)
すなわち、親クラス・自クラスのインスタンスフィールドから値を取得できない。
thisそのものを使用することも出来ない。


また、親クラスのコンストラクター呼び出し(super())の前に自分のクラス(子クラス)のフィールドに値を設定することが出来るようになった。

親クラスのコンストラクターからprotectedなメソッド(子クラスでオーバーライドされているメソッド)を呼び出しても、
今までは子クラスのフィールドを使用することは出来なかった。
(子クラスのフィールドが初期化されるのは、親クラスの初期化(コンストラクター呼び出し)が完了してからだったので)

Java25以降では、コンストラクター内でsuper()呼び出し前に値を設定したフィールドに関しては、親コンストラクター内で値が取得できる。

親コンストラクター内で子クラスのフィールドの値を取得する例


this()の呼び出し前に計算を行う例。

public class BeforeThisExample {

    private String key;
    private String value;

    public BeforeThisExample(String s) {
        String key, value;
        int n = s.indexOf("=");
        if (n >= 0) {
            key = s.substring(0, n);
            value = s.substring(n + 1);
        } else {
            key = s;
            value = null;
        }
        this(key, value);
    }

    public BeforeThisExample(String key, String value) {
        this.key = key;
        this.value = value;
    }
}

this()・super()の呼び出しの前にthisを使うとコンパイルエラーになる。

public class BeforeThisExample {

    public BeforeThisExample(int n) {
        System.out.println(this); // NG
        this();
    }

    public BeforeThisExample() {
    }
}

親コンストラクター内で子クラスのフィールドの値を取得する例

Java25以降では、親クラスのコンストラクター(superメソッド)を呼び出す前に自分のクラスのフィールドを初期化することが出来る。
具体的には、「super();」の前に「this.フィールド = 値;」を書くことが出来る。

そして、その値は親クラスのコンストラクターの中で取得することが出来る。

abstract class Super {

    public Super() {
        // コンストラクターの中で、子クラスのフィールドの値を取得
        System.out.println("Super constructor: childField1=" + getChildField1());
        System.out.println("Super constructor: childField2=" + getChildField2());
    }

    protected abstract String getChildField1();

    protected abstract String getChildField2();
}
class Child extends Super {

    private String childField1 = "child.field1";
    private String childField2;

    public Child() {
        System.out.println("Child constructor start");

        this.childField2 = "child.field2 by constructor";

        super();

        System.out.println("Child constructor end");
    }

    @Override
    protected String getChildField1() {
        return this.childField1;
    }

    @Override
    protected String getChildField2() {
        return this.childField2;
    }
}

new Child()の実行結果

Child constructor start
Super constructor: childField1=null
Super constructor: childField2=child.field2 by constructor
Child constructor end

(Childの)コンストラクターの中でsuperメソッド呼び出し前に値をセットしたフィールド(上記の例のchildField2)は、親コンストラクターで値が取得できる。

フィールド定義箇所で初期値を設定したフィールド(上記の例のchildField1)の値は、親コンストラクターで取得できない。(nullが取れている)


なお、superメソッドの呼び出し前にフィールドを初期化する文を書く場合、フィールド定義箇所で初期値を指定することは出来ない。

class Child extends Super {
〜
    private String childField2 = "child.field2";

    public Child() {
        this.childField2 = "child.field2 by constructor"; // NG

        super();
    }
〜
}

↑「Cannot assign field 'childField2' in an early construction context, because it has an initializer」というコンパイルエラー


Java目次へ戻る / 新機能へ戻る / 技術メモへ戻る
メールの送信先:ひしだま