Javaでの擬似自分型(Simulated self type)

Javaで自分型、つまり自分自身の型を表現する方法について紹介します。
メリットを実感してもらうためにメソッドチェーンを使って説明していきます。
メソッドチェーンというのは

```java
Person person = new Person().setFirstName("太郎").setLastName("日本橋");
```

のようにSetterのように状態を更新するメソッドの返り値を典型的なSetterのように`void`にするのではなく、自分自身を返すことによってメソッドを連鎖させて記述する手法です。
先程のコードはメソッドチェーンを使わないと以下のように書くのではないでしょうか。
Javaのコードなので仕方ないですが、なんだか冗長に感じますね。

```java
Person person = new Person();
person.setFirstName("太郎");
person.setLastName("日本橋");
```

ただ、Javaの場合、「自分自身」を表す型がないのでメソッドチェーンと継承を組み合わせると少し複雑なことをしないといけません。
それが今回紹介する　**擬似自分型(Simulated self type)**　です。

### 他の言語ではどうなっているか

TypeScriptだと、メソッドの戻り値として自分自身の型を表せる`this`という型があり、以下のようなコードが書けます。

```typescript
class SuperClass{
  public firstName:string = "";
  public lastName: string = "";

  public setFirstName(firstName:string): this{
    this.firstName = firstName;
    return this;
  }
  public setLastName(lastName: string): this{
    this.lastName = lastName;
    return this;
  }
}

const superClass:SuperClass = new SuperClass().setFirstName("太郎").setLastName("日本橋")

class SubClass extends SuperClass{
  public email: string = "";

  public setEmail(email: string): this{
    this.email = email;
    return this;
  }
}

const subClass:SubClass = new SubClass().setFirstName("花子").setLastName("丸の内").setEmail("hanako.marunouchi@example.com")
```

大事な点としては最後の行で`new SubClass().setFirstName("花子")`の戻り値の型が`SubClass`になっていることです。
このコードを読むと「何を当たり前のことを。。。」と思うかもしれませんが、Javaにはこの`this`がないので、これから解説していくことになる　**擬似自分型(Simulated self type)**　という少しややこしい手法を使わなくてはならないということを解説していきます。


## Javaでの素朴な方法の問題点

Javaの場合どうなるのかというのをみていきます。
まず、メソッドチェーンを普通に実装してみます。
`main()`メソッドの中で`SuperClass`のオブジェクトを生成してメソッドチェーンで値を設定したあと、フィールドを読み取って標準出力しています。
実行してみると特に問題なく出力されます。

あとで継承を使うのでクラス名は`SuperClass`としておきます。

```java
class Main {
  public static void main(String[] args) {
    SuperClass superClass = new SuperClass().setFirstName("太郎").setLastName("日本橋");
    System.out.println("私の名前は%s %sです。".formatted(superClass.getLastName(), superClass.getFirstName()));
  }
}

class SuperClass {
  private String firstName;
  private String lastName;

  // Setterが自分自身を返すようにしてメソッドチェーンを実現する
  public SuperClass setFirstName(String firstName) {
    this.firstName = firstName;
    return this;
  }

  public SuperClass setLastName(String lastName) {
    this.lastName = lastName;
    return this;
  }

  // Getterが続く
  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

}
```

### 継承してみる

継承してみましょう。親クラスの名前を`SuperClass`としているのでサブクラスの名前は当然`SubClass`です。

```java
class SubClass extends SuperClass {
  // SubClassはメールアドレスも持っている
  private String email;

  public SubClass setEmail(String email) {
    this.email = email;
    return this;
  }

  // Getterが続く
  public String getEmail() {
    return email;
  }

}
```

この`SubClass`に名前、苗字、メールアドレスの順番で設定して、先程のように標準出力してみましょう。

```java
class Main {
  public static void main(String[] args) {
    SubClass subClass = new SubClass()
        .setFirstName("花子")
        .setLastName("丸の内")
        .setEmail("hanako.marunouchi@example.com");
    System.out.println(
        "私の名前は%s %sです。宛先はこちら%s。"
            .formatted(subClass.getLastName(), subClass.getFirstName(), subClass.getEmail()));
  }
}
```

VSCodeとか使ってると`The method setEmail(String) is undefined for the type SuperClass`って警告が出ますね。
`javac ./Main.java`とコンパイルしてみようとしても以下のようなエラーが出ます。

```
./Main.java:11: エラー: シンボルを見つけられません
        .setEmail("hanako.marunouchi@example.com");
        ^
  シンボル:   メソッド setEmail(String)
  場所: クラス SuperClass
エラー1個
```

まあ、`setFirstName()`と`setLastName()`の戻り値がSuperクラスなので残念ながら当然です。

聡明な読者は気づいたでしょう。
「`setFirstName()`と`setLastName()`をオーバーライドすればいいじゃん！」

```java
class SubClass extends SuperClass {
  // SubClassはメールアドレスも持っている
  private String email;

  public SubClass setEmail(String email) {
    this.email = email;
    return this;
  }

  @Override
  public SubClass setFirstName(String firstName) {
    setFirstName(firstName);
    return this;
  }

  @Override
  public SubClass setLastName(String lastName) {
    setLastName(lastName);
    return this;
  }

  // Getterが続く
  public String getEmail() {
    return email;
  }

}
```

今度はコンパイルできましたね。めでたしめでたし！とはいきません。
こんなコードじゃ何のために継承を使ったのか全くわかりませんね。
`SuperClass`のフィールドが少ないので、この程度で済んでいますが、フィールドが多かった場合苦行でしかないでしょう。
くそッ！Javaにも自分型があれば、、、と思っているでしょう。

この問題を何とかしてくれるのが　**擬似自分型(Simulated self type)**　です。

## 解決策としての擬似自分型(Simulated self type)

### 実装を見てみる

まあ、一旦コードを見てくれ、話はそれからなんですよ。

```java
abstract class SuperClass<T extends SuperClass<T>> {
  private String firstName;
  private String lastName;

  // Setterが自分自身を返すようにしてメソッドチェーンを実現する
  public T setFirstName(String firstName) {
    this.firstName = firstName;
    return self();
  }

  public T setLastName(String lastName) {
    this.lastName = lastName;
    return self();
  }

  // 自分自身を返すようにSubClassに実装してもらう
  public abstract T self();

  // Getterが続く
  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

}

class SubClass extends SuperClass<SubClass> {
  // SubClassはメールアドレスも持っている
  private String email;

  public SubClass setEmail(String email) {
    this.email = email;
    return this;
  }

  @Override
  public SubClass self() {
    return this;
  }

  // Getterが続く
  public String getEmail() {
    return email;
  }

}
```

`SuperClass`が抽象クラスになったり、新しく`self()`とかいうメソッドを実装しないといけなくなったり、よくわからんジェネリクスがついたりして、よくわかんないですね。
ただ、なんかよくわからんけどコンパイルできます。めでたしめでたし！

今回は実際にめでたいのでこれから説明していきます。

まず、最初に理解して欲しい点として、先程までと違って今回`SubClass`がオーバーライドしないといけないのは、自分自身を返すだけの`self()`メソッドだけです。
これで`SuperClass`のフィールドが膨大だとしても`SubClass`がオーバーライドしないといけないメソッドの数が増えることはありません。
これが擬似自分型を使うメリットです。

### どうしてコンパイルが通るの？

次に、どうしてコンパイルが通るのかという解説をします(これからコード中のGetterは省略します)。
以下のように`SuperClass`の`setFirstName()`メソッドは`SuperClass<T>`を拡張したクラスの`T`クラスを返すことになっています。

```java
abstract class SuperClass<T extends SuperClass<T>> {
  private String firstName;
  private String lastName;

  public T setFirstName(String firstName) {
    this.firstName = firstName;
    return self();
  }

  public T setLastName(String lastName) {
    this.lastName = lastName;
    return self();
  }

  public abstract T self();

}
```

そして、`SubClass`のコードは以下のようになっています。
`SubClass`は`SuperClass<SubClass>`を継承しているので、さきほどのジェネリクスでの`T`は今回の場合`SubClass`になります(`SubClass`が`SuperClass<SubClass>`を拡張していることは定義より自明でしょう)。
なので、`setFirstName()`メソッドは`SubClass`を返します。
`self()`メソッドは単に`this`を返せばおしまいです。
スッキリしましたね。

```java
class SubClass extends SuperClass<SubClass> {
  // SubClassはメールアドレスも持っている
  private String email;

  public SubClass setEmail(String email) {
    this.email = email;
    return this;
  }

  @Override
  public SubClass self() {
    return this;
  }

}
```


## 具体例(継承を伴うBuilderパターン)

次に具体例についてみていきます。
実際にJavでWebアプリケーションを作る時はSpringを使用することが多いと思うので、DIコンテナにインスタンスの生成を管理してもらうことになると思うので、自分で書く機会は少ないかと思いますが、Builderパターンでの使用例を見ていきましょう(会社で共通ライブラリとか作るなら使えるかも)。

Builderパターンというと以下のようにメソッドチェーンでBuilderの値を設定していって、最後に`build()`メソッドでお目当てのクラスを生成する手法です。

```java
Person person = new PersonBuilder().setFirstName("太郎").setLastName("日本橋").build();
```

では、また`SuperClass`のみの場合にどうなるのか見てみましょう。

まずは呼び出し側のコードです。

```java
class Main {
  public static void main(String[] args) {
    SuperClass superClass = new SuperClass.Builder().setFirstName("太郎").setLastName("日本橋").build();
    System.out.println("私の名前は%s %sです。".formatted(superClass.getLastName(),
        superClass.getFirstName()));
  }
}
```

ちゃんとBuilderパターンになっていますね。

次は`SuperClass`の実装です。
重要な点は、

- staticクラスとして`Builder`が追加されたこと
- コンストラクタがプライベートになったこと
- `SuperClass`がSetterを持たないので不変クラスになったこと

あたりです。

```java
class SuperClass {
  private String firstName;
  private String lastName;

  // コンストラクタはプライベート
  private SuperClass(Builder builder) {
    this.firstName = builder.firstName;
    this.lastName = builder.lastName;
  }

  // Getterのみ、Setterは存在しない
  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public static class Builder {
    private String firstName;
    private String lastName;

    public Builder setFirstName(String firstName) {
      this.firstName = firstName;
      return this;
    }

    public Builder setLastName(String lastName) {
      this.lastName = lastName;
      return this;
    }

    public SuperClass build() {
      SuperClass superClass = new SuperClass(this);
      return superClass;
    }
  }

}
```

`SuperClass`を継承するとうまくいかないのはBuilderパターンの前の解説から明らかでしょう。

うまいこと継承したコードは以下のようになります。
くどくなるので説明はしませんが、先程の話を思い出しながら読めばすらすら読めると思います。

```java
class Main {
  public static void main(String[] args) {
    SubClass subClass = new SubClass.Builder().setFirstName("花子").setLastName("丸の内").setEmail("hanako.marunouchi@example.com").build();
    System.out.println(
        "私の名前は%s %sです。宛先はこちら%s。"
            .formatted(subClass.getLastName(), subClass.getFirstName(), subClass.getEmail()));
  }
}

abstract class SuperClass {
  private String firstName;
  private String lastName;

  // コンストラクタはprotectedになっちゃった
  protected SuperClass(Builder<?> builder) {
    this.firstName = builder.firstName;
    this.lastName = builder.lastName;
  }

  // Getterのみ、Setterは存在しない
  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public static abstract class Builder<T extends Builder<T>> {
    private String firstName;
    private String lastName;

    public T setFirstName(String firstName) {
      this.firstName = firstName;
      return self();
    }

    public T setLastName(String lastName) {
      this.lastName = lastName;
      return self();
    }

    public abstract SuperClass build();

    protected abstract T self();
  }

}

class SubClass extends SuperClass {
  // SubClassはメールアドレスも持っている
  private String email;

  // こっちのコンストラクタはプライベート
  private SubClass(Builder builder) {
    super(builder);
    email = builder.email;
  }

  public static class Builder extends SuperClass.Builder<Builder> {

    private String email;

    public Builder setEmail(String email) {
      this.email = email;
      return this;
    }

    @Override
    public SubClass build() {
      return new SubClass(this);
    }

    @Override
    protected Builder self() {
      return this;
    }

  }

  // Getterのみ、Setterは存在しない
  public String getEmail() {
    return email;
  }

}
```

## 標準パッケージでの実装例(enum)

ちなみにOpenJDKのenumも擬似自分型を使って実装されているっぽいです。
[GitHubのリンクはこちら](https://github.com/AdoptOpenJDK/openjdk-jdk11/blob/master/src/java.base/share/classes/java/lang/Enum.java)

## まとめ

Builderパターンで継承を使うときは擬似自分型を使おう！(実はEffective Javaの受け売りです。。。)
まあ、Builderパターンに限らずJavaでメソッドチェーンを使うときはもしかしたら使うことになるかもしれないですね。
あまりわかりやすい手法ではないので多用するのは良くないかなとは思います。
共通ライブラリなど、普段開発者が直接いじる機会が少ない箇所に絞るべきかなーというのが素直な感想です。
私自身、この擬似自分型を読んだだけではにいまいち理解しきれなかったので、こうしてまとめることでやっと理解できました。
でも、使い所を選べば非常に有用な手法だとは思うので、ここぞというときに使えるように頭の片隅には置いておきたいですね。

## 参考文献

[Effective Java 第3版](https://www.amazon.co.jp/Effective-Java-%E7%AC%AC3%E7%89%88-Joshua-Bloch/dp/4621303252)