Java の Records に Compact Constructors と呼ばれる機能があります。
record Foo(int x) { Foo { x = 100; } }
Compact Constructors の中ではフィールドを初期化する処理は書けません。コンパイルエラーになります。
record Foo(int x) { Foo { this.x = 100; // エラー: final変数xに値を代入することはできません } }
ふつうのコンストラクタの場合は問題なし。
record Foo(int x) { Foo(int x) { this.x = 100; // OK } }
なんであえてコンパイルエラーになるようにしたんだろうか...?
Records の Compact Constructors の中でフィールドを初期化したときにコンパイルエラーにしたのはなぜなんだろうか
— kntmr (@knt_mr) 2020年10月4日
そのあたりの理由というか経緯は見つけられませんでしたが、とりあえず最初の Compact Constructors 版を javap してみました。
> java --version openjdk 15 2020-09-15 OpenJDK Runtime Environment (build 15+36-1562) OpenJDK 64-Bit Server VM (build 15+36-1562, mixed mode, sharing) > javac --enable-preview --release 15 Foo.java > javap -c Foo.class Compiled from "Foo.java" final class Foo extends java.lang.Record { Foo(int); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Record."<init>":()V 4: bipush 100 6: istore_1 7: aload_0 8: iload_1 9: putfield #7 // Field x:I 12: return public final java.lang.String toString(); Code: 0: aload_0 1: invokedynamic #13, 0 // InvokeDynamic #0:toString:(LFoo;)Ljava/lang/String; 6: areturn public final int hashCode(); Code: 0: aload_0 1: invokedynamic #17, 0 // InvokeDynamic #0:hashCode:(LFoo;)I 6: ireturn public final boolean equals(java.lang.Object); Code: 0: aload_0 1: aload_1 2: invokedynamic #21, 0 // InvokeDynamic #0:equals:(LFoo;Ljava/lang/Object;)Z 7: ireturn public int x(); Code: 0: aload_0 1: getfield #7 // Field x:I 4: ireturn }
どうやらコンストラクタのローカル変数に代入して、そのあとにインスタンスフィールドを初期化してるっぽいです。おそらく、Compact Constructors に書いた処理はインスタンスフィールドの初期化の前に差し込まれるのではないかと思います。(想像で書いてるので正確ではないと思います)
最初の Compact Constructors 版の record は次のようなクラスになっていると思われます。
class Foo extends Record { private final int x; public Foo(int x) { x = 100; this.x = x; } // ... (略) }
ちなみにふつうのコンストラクタ版を javap するとこういう感じ。
> javap -c Foo.class Compiled from "Foo.java" final class Foo extends java.lang.Record { Foo(int); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Record."<init>":()V 4: aload_0 5: bipush 100 7: putfield #7 // Field x:I 10: return ... (略)
参考
追記
ここにいろいろ書いてありました。(原文)
コンパクト・コンストラクタを宣言する目的は、カノニカル・コンストラクタの本体で必要となる、検証や正規化のみを行うコードを提供することにあります。その他の初期化コードはコンパイラが提供します。
あと、このあたりにもいろいろ書いてあるっぽい。ちゃんと読んでないですが...。
8.10.4 Record Constructor Declarations - Records - Oracle Help Center