JavaのRecord使い方まとめ

最新のLTSバージョンでもレコードクラスが使えるようになったので使い方メモです。

基本

Java14でPreview ReleaseされてJava16から正式に追加された機能で、こんな感じで書くと、

public record User(String name, int age) {
}

こんな感じのクラスを定義したのと同じ感じになります。

public final class User {
    private final String name;
    private final int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String name() {
        return name;
    }

    public int age() {
        return age;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) return true;
        if (obj == null || obj.getClass() != this.getClass()) return false;
        var that = (User) obj;
        return Objects.equals(this.name, that.name) &&
                this.age == that.age;
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "User[" +
                "name=" + name + ", " +
                "age=" + age + ']';
    }
}

ちなみにこのコードはIntelliJの変換機能で普通のクラスに自動変換したもの。

フィールドの宣言

名前の横の括弧の中に列挙して定義します。
正式名称はレコードコンポーネント。

public record User(String name, int age) {
}

ジェネリクスも使えます。

public record Pair<L, R>(L left, R right) {
}

コンストラクタを上書きする

コンストラクタを上書きできるのでコンストラクタで値のチェックをしたい場合に使えます。

public record User(String name, int age) {
    public User(String name, int age) {
        if (age < 0) {
            throw new IllegalArgumentException("Error");
        }
        this.name = name;
        this.age = age;
    }
}

代入する値が引数の値そのままの場合は引数と代入する処理を省略した書き方で記述できます。

public record User(String name, int age) {
    public User {
        if (age < 0) {
            throw new IllegalArgumentException("Error");
        }
    }
}

自動生成されるメソッドを上書きする

自動生成されるメソッドは上書き可。
equalstoStringをカスタマイズしたい場合やGetterで任意の処理を行いたい場合に上書きします。

public record User(String name, int age, List<String> follower) {
    @Override
    public List<String> follower() {
        return Collections.unmodifiableList(follower);
    }
}

独自メソッドを定義する

自由に定義可能。

public record User(String name, int age) {
    public String toUpperCase() {
        return name.toUpperCase();
    }
}

フィールドはstaticだけ

普通のクラスっぽいフィールドはstaticなフィールドのみ定義可能。
staticが付いていないとコンパイルエラーになる。

BeanValidation

フィールド宣言のところに一緒に指定すればOK。

public record User(@NotNull @Size(max = 8) String name, @Min(0) int age) {
}

Jackson

2.13系で試してみた感じだと割としれっと使えるっぽいですが一部機能が未対応らしくアノテーションが効かなかったりするので注意。
GitHubのIssueにRecordってラベルがある ので想定外の動きしてる場合は確認しましょう。

IntelliJで自動変換できる

IntelliJで普通のクラスと相互変換できます。
普通のクラスがRecordへの変換が可能な場合は、Recordにできるけど?って警告が出ます。