(注:このブログはもう更新していません)この日記は私的なものであり所属会社の見解とは無関係です。 GitHub: takahashikzn

[クラウド帳票エンジンDocurain]

recordのコンストラクタを隠す

public record Foo(String a, int b) {}

このようなレコードクラスがあったとき、このコンストラクタを隠すことはできません。

ただ、

public record Foo(String a, int b) {

    public static final List<Foo> foos;

    static {
        List<Foo> list = new ArrayList<>();

        for (var x : ...)
            list.add(new Foo(x.a, x.b));

        foos = Collections.unmodifiableList(list);
    }
}

のように固定の数のFooのインスタンスを作りたいとき、レコードクラスのコンストラクタは公開されています。ではどうするか。

結論

このようになりました。

public record Foo(@Deprecated __private__ _unused_, String a, int b) {

    private record __private__() {
        static final __priavte__ instance = new __priavte__();
    }

    public static final List<Foo> foos;

    static {
        List<Foo> list = new ArrayList<>();

        for (var x : ...)
            list.add(new Foo(__private__.instance, x.a, x.b));

        foos = Collections.unmodifiableList(list);
    }
}

Foo.__private__ クラスは外部からアクセス不能であり、結果としてFooクラスのコンストラクタを外部から実行することはできません。 これでコンパイル時チェックが可能になりました。_unused_というゴミプロパティが残るのが唯一の欠点ですが、実用上問題ありません。

追記

nullを渡せば良いことを忘れていました。つまりこれはコンパイルエラーになりません。

new Foo(null, "a", 1);   /(^o^)\

何とかするとしても

public record Foo(@org.jetbrains.annotations.NotNull @Deprecated __private__ _unused_, String a, int b) {
    ...
}

ってやるくらいですかね…

まあ、仮にコンストラクタをprivateにできたとしてもリフレクションで強引に呼び出すことは依然として可能なわけで。 上記のようにしておけば、「間違って呼び出してしまう」というコードを書いてしまう可能性を、ほぼゼロにはできると思います。