【Java】Value-Based Classってなんやねん

# はじめに

Java 17のソースコードで遊んでいると、下記コードの`synchronized`ブロックで警告が出ました。
```SynchronizedSample.java
public class SynchronizedSample {

	Integer integerObject = 1;

	void sampleMethod() {

		synchronized(integerObject) {

			System.out.println("a");
		}
	}
}
```
:::note warn
`Integer is a value-based type which is a discouraged argument for the synchronized statement`
:::
「`integer`は`value-based`型で`synchronized`構文では推奨されないよ」ですって。
「なんじゃこりゃ」と思い調べてみたので記事にします。


# Value-based ってなんやねん

`Value-based Class`というものがあるそうです。

https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/doc-files/ValueBased.html

（↓我らがDeepL翻訳で訳してちょこちょこ手を加えただけ）

`Value-based Class`は次のような特性をもちます。
- クラスは`final`なインスタンスのフィールドのみを宣言する（ただし、これらのフィールドには変更可能なオブジェクトへの参照が含まれている可能性がある）。
- クラスの`equals`、`hashCode`、`toString`の実装は、インスタンスのIDからではなく、クラスのインスタンス・フィールド（およびそれらが参照するオブジェクトのメンバ）の値のみから結果を計算する。
- クラスのメソッドは、インスタンスが等しい場合は自由に置換可能として扱う。つまり、`equals`で等しい2つのインスタンス`x`と`y`を入れ替えても、メソッドの動作に目に見える変化は生じない。
- クラスはインスタンスの`monitor`を使った同期を行わない。
- クラスは利用可能なコンストラクタを宣言していない（あるいは非推奨）。
- このクラスは、各メソッド呼び出し時に一意のIDを約束するインスタンス生成のメカニズムを提供しない。特に、ファクトリーメソッドの生成時は、独立に生成された2つのインスタンスが`equals()`に従って等しい場合、`==` に従って等しくなる可能性を許容しなければならない。
- クラスは`final`である。
- クラスは`Object`か、インスタンスフィールドやインスタンス初期化子を宣言せずコンストラクタが空である抽象クラスの階層のいずれかを継承している。

例えば、`java.lang.Integer`、`java.time.LocalDate`のようなクラスです。

`Value-based Class`の2つのインスタンスが(`equals`に従って)等しいとき、プログラムは、参照の等価性によって直接的にであろうと、同期、IDハッシュ、直列化、あるいは他のIDに依存するメカニズムによって間接的にであろうと、それらのIDを区別しようと試みるべきではない。

なぜなら、プログラマーは関連する`monitor`の排他的な所有権を保証できないからである。

`Value-based Class`の ID 関連の動作は、将来のリリースで変更される可能性がある。例えば、同期に失敗する可能性がある。

（翻訳ここまで）

## 要するに
`Value-based Class`は一意のIDを約束するコンストラクタを提供せず、裏ではIDではなく値をもとにいろいろと操作が行われるオブジェクトのことを指すのでしょうかね。

## なんで警告なのか

オブジェクトを一意に識別するIDを持たない`Value Class`というものが導入されています（まだPreview版のはず）。

https://openjdk.org/jeps/401

`Value Class`はオブジェクトのIDを持たないためロックに使えません。

>Some classes in the standard library have been designated value-based, with the understanding that they would become value classes in a future release.
> ...
> In anticipation of this feature we already added warnings about potential behavioral incompatibilities for value class candidates in javac and HotSpot, via JEP 390.

`Value-based Class`は将来のリリースで`Value Class`になる可能性があります。

この機能を見越して、JEP 390を通じてjavacとHotSpotにおいて`Value Class`の候補に対して潜在的な動作の非互換性に関する警告をすでに追加しています。

ということでした。

### ちなみにValue Classとは（ざっくりと）

`Value Class`は`value`キーワードを使って宣言します。

```java
value class USDollars implements Comparable<USDollars> {
    private int cs;
    private USDollars(int cs) { this.cs = cs; }

    public USDollars(int dollars, int cents) {
        this(dollars * 100 + (dollars < 0 ? -cents : cents));
    }

    public int dollars() { return cs/100; }
    public int cents() { return Math.abs(cs%100); }

    public USDollars plus(USDollars that) {
        return new USDollars(cs + that.cs);
    }

    public int compareTo(USDollars that) { ... }
    public String toString() { ... }
}
```

`value`キーワードで宣言したクラスを`==`で比較する時、フィールドの値を比較するようになります（プリミティブの場合はビットパターンで、その他は再帰的に`==`で比較）。

```java
USDollars d1 = new USDollars(3,95);
USDollars d2 = new USDollars(3,95).plus(new USDollars(0,0));
assert d1 == d2;
```
`value`をつけないクラスの場合に:arrow_up:は`false`と評価されますが、クラスの宣言に`value`をつけることで`true`と評価されることが期待されます。

また、このような`Value Class`のインスタンスのことを「値オブジェクト(`value object`)」と言います。

## そもそも
`Value-based Class`の特徴を考えると、@tchaikovsky1026さんのおっしゃる通りかなと思います。
> Integerは値オブジェクトであり, インスタンスが色んなところで共有されて使われるのが普通(むしろ共有されるべき)なので, synchronized(Object)との相性は最悪だと思います.

# おわりに
まちがっているところとか補足あれば、ご指摘いただけると嬉しいです。

<hr>
（2023/12/8）
コメントを投稿いただいた@tchaikovsky1026さん、@skrbさんありがとうございました。値オブジェクトというものを知りませんでした。勉強になりました:bow: