new Object() {}で一時的クラスを作成する

# はじめに

Javaの`Object`クラスはすべてのクラスのスーパークラスで非常に重要なクラスですが、それ自体を`new`することはあまりないのではないでしょうか？
この記事では`new Object()`が有意義となる二つのコード例を紹介します。

# メソッド内にメソッドを定義する


以下は「[Javaで数学の組み合わせ(Combination)を実装する - Qiita](https://qiita.com/5zm/items/949bf0721e8324f0ee35#comments)」のコメントに記載されたコード例です。文字列の配列`data`から`k`個取る組み合わせを列挙して返すメソッドです。

```java
    static List<String[]> combination(String[] data, int k) {
        List<String[]> result = new ArrayList<String[]>();
        combination(data, 0, new String[k], 0, result);
        return result;
    }

    static void combination(String[] data, int di, String[] comb, int ci, List<String[]> result) {
        if (ci == comb.length) {
            result.add(comb.clone());
            return;
        }
        for ( ; di <= data.length - (comb.length - ci); di++) {
            comb[ci] = data[di];
            combination(data, di + 1, comb, ci + 1, result);
        }
    }
```

2つのメソッドから構成されていて、外部から直接呼び出されるのは2引数の`combination`で、5引数の`combination`は内部的に利用されています。5引数の`combination`は自分自身を再起呼び出ししていますが、5引数の内、3引数(`data`, `comb`, `result`)は全く同じ値を引数として指定しています。
もしJavaがメソッドを入れ子にして定義できるのであれば、再起呼び出し時に同じ引数を記述しなくても済むはずです。残念ながらJavaではメソッドを入れ子にすることができません。
そこで`new Object()`を使ってこれを書き直してみます。

```java
    static List<String[]> combination(String[] data, int k) {
        int length = data.length;
        List<String[]> result = new ArrayList<String[]>();
        String[] comb = new String[k];
        new Object() {
            void combination(int di, int ci) {
                if (ci == k) {
                    result.add(comb.clone());
                    return;
                }
                for (; di <= length - (k - ci); di++) {
                    comb[ci] = data[di];
                    combination(di + 1, ci + 1);
                }
            }
        }.combination(0, 0);
        return result;
    }
```

5引数の`combination`は`new Object() {}`の内側に記述して固定の3引数(`data`, `comb`, `result`)は削除しました。
このコードの`new Object() {}`は無名のインナークラスを定義して、同時にそのクラスのインスタンスを作成します。`new Object() {}`の後の`.combination(0, 0)`はインナークラス内で定義されている`combination(int di, int ci)`を呼び出します。
固定の3変数(`data`, `comb`, `result`)はインナークラスの中から参照可能なので、呼び出しごとに引数で渡してやる必要はなくなります。
`new Object()`で無駄なインスタンスを作っていますが、1個作るだけなのでオーバーヘッドは小さいです。再起呼び出し時の引数が減るので、スタックの消費量は減ります。

# 一時的なデータ保管のためのクラス

以下のようなクラスがあったとします。

```java
public class Person {

    private final String name;
    private final String age;

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

    public String getName() {
        return this.name;
    }

    public String getAge() {
        return this.age;
    }
}
```

さらに以下のような`Person`のリストがあったとします。

```java
List<Person> list = List.of(
    new Person("Yamada", "18"),
    new Person("Ichikawa", "72"),
    new Person("Sato", "39"),
    new Person("Tanaka", "9"));
```

これを`age`で昇順にソートすることを考えてみます。`age`は文字列なので整数としてソートするためにはこのように記述する必要があります。

```java
    List<Person> result = list.stream()
        .sorted(Comparator.comparingInt(p -> Integer.parseInt(p.getAge())))
        .collect(Collectors.toList());
```

これはこれでうまくいきますが、値を比較する都度`Integer.parseInt(p.getAge())`が2回ずつ実行されてしまうので、あまり効率がよくありません。高速なソートのアルゴリズムでも`Integer.parseInt`は $ 2n \log n $ 回実行されてしまいます。

## Map.Entry

`Person`のインスタンスと`age`を整数化したものをペアにすれば、この問題は解決します。こういう時、一時的な格納場所としてよく利用されるのが`Map.Entry`クラスです。

```java
    List<Person> result = list.stream()
        .map(p -> Map.entry(Integer.parseInt(p.getAge()), p))
        .sorted(Comparator.comparingInt(Entry::getKey))
        .map(Entry::getValue)
        .collect(Collectors.toList());
```
整数の`age`の計算は1回で済みますが、`Map.Entry`が使われている所が直観的にわかりにくいです。

## Records
Java14ではデータ保管のための不変クラスを簡単に定義できるRecordsという機能が追加されています。これを使うとこうなります。

```java
    record PersonWithAge(Person p, int age) {
        PersonWithAge(Person p) {
            this(p, Integer.parseInt(p.getAge()));
        }
    }

    List<Person> result = list.stream()
        .map(PersonWithAge::new)
        .sorted(Comparator.comparingInt(PersonWithAge::age))
        .map(PersonWithAge::p)
        .collect(Collectors.toList());
```

わかりやすくなりましたが、一時保管用のクラスを定義していて少しおおげさな感じもします。

## new Object()
`new Object()`を使うとこうなります。

```java
    List<Person> result = list.stream()
        .map(p -> new Object() {
            int intAge = Integer.parseInt(p.getAge());
            Person person = p;
        })
        .sorted(Comparator.comparingInt(obj -> obj.intAge))
        .map(obj -> obj.person)
        .collect(Collectors.toList());
```

`new Object() {...}`はこの式の中だけで有効な一時的保管場所です。無名のクラスなのですが、この式の中では`intAge`と`person`というフィールドを持った一人前のクラスであると認識されています。

# まとめ

いかがだったでしょうか？`Object`クラスにもいろいろな使い方あることがわかっていただけたと思います。トリッキーな感じがするのであまりおすすめはできませんが、私はこういう書き方ができると知ってからけっこう使っています。