Java17でEither書く

# はじめに
たまたま最近、業務でエラーかノーマルを保持するオブジェクトのレビューが上がってきた時に、「Eitherにすれば？」と言ったのだが、Javaには`Optional`はあるが`Either`に相当するようなクラスがない。
作るとしたらこんな感じかな〜、というのをリリースされたLTS版Java17を使いながら書いてみる。（結局、レビュー指摘はコミットされなかったが・・・。）


# Either

```java:Either.java
import java.util.function.Function;
import java.util.function.Consumer;

//Eitherインターフェイス
public sealed interface Either<L,R> permits Either.Left,Either.Right{
  public boolean isLeft();

  default boolean isRight(){
    return !this.isLeft();
  }

  public <A> Either<L,A> flatMap(Function<R,Either<L,A>> f);

  public default <A> Either<L,A> map(Function<R,A> f){
    return this.flatMap((R r) -> Either.rightOf(f.apply(r)));
  }

  public void foreach(Consumer<R> action);

  public static <L,R> Either<L,R> leftOf(L value){
    return new Left<L,R>(value);
  }

  public static <L,R> Either<L,R> rightOf(R value){
    return new Right<L,R>(value);
  }

  //Right型
  public static final record Right<L,R>(R value) implements Either<L,R>{
    public boolean isLeft(){
      return false;
    }
    public <A> Either<L,A> flatMap(Function<R,Either<L,A>> f){
      return f.apply(this.value);
    }
    public void foreach(Consumer<R> action){
      action.accept(this.value);
    }
  }

  //Left型
  public static final record Left<L,R>(L value) implements Either<L,R>{
    public boolean isLeft(){
      return true;
    }
    public <A> Either<L,A> flatMap(Function<R,Either<L,A>> f){
      return new Left<L,A>(this.value);
    }
    public void foreach(Consumer<R> action){
      return;
    }
  }
}
```

## sealed
インターフェイスとクラスに`sealed/permits`をつけて継承できるものを制限できるようになった。ここでは`Either`インターフェイスの継承を`Either.Right` と `Either.Left`に限定している。

## record
Scalaの`case class`っぽいやつ。`equals`や`hashCode`,`toString`メソッドなどが生える。
`record`は`final`なフィールドで、イミュータブルになる。（セッターに該当するメソッドは生えない）

# Main
Eitherを使う方。

```java:Main.java
import static java.lang.System.out;
public class Main{
  public static void main(String... args){
    //型推論する場合でも、結局は型を教えてあげないといけない。
    final var e1 = Either.<String,Integer>rightOf(10);

    //型宣言の場合
    final Either<String,Integer> e2 = Either.leftOf("Error!");

    //flatMap/map でscalaのforの代わり
    final var e3 = e1.flatMap(n1 -> e2.map(n2 -> n1 + n2));

    out.println(e3) //Left[value=Error!]
  }
}
```

Scalaと異なり、`Nothing型`がないので、Eitherオブジェクトの生成は、ちょっと冗長になってしまう。
また、`for式`(`do記法`)もないので、自力でflatMap/mapでコンテキスト保った処理を書く必要があるので、やや使いにくい。

## Scala相当

Scala相当だとこんな形になるかと。

```scala:Scala
val e1:Either[String,Int] = Right(10)
val e2:Either[String,Int] = Left("Error!")
val e3 = for (n1 <- e1; n2 <- e2) yield (n1 + n2)
println(e3)
```

# おまけでプレビュー版の`switch`パターンマッチ

プレビュー版機能なので、コンパイラとJVMの両方に`--enable-preview`オプションをつける。

```java:Main
import static java.lang.System.out;
public class Main{
  public static void main(String... args){
    
    final var e1 = Either.<String,Integer>rightOf(10);

    int ret = switch(e1) {
      case Either.Right<String,Integer> right -> right.value();
      case Either.Left<String,Integer> left -> 0;
    }
    out.println(ret) // 10
  }
}

```

```bash
$ javac --enable-preview --release 17 Main.java Either.java
$ java --enable-preview Main
```

## 終わりに
Javaの`interface`のデフォルトメソッドを、Scalaの`trait`と同じ感覚でミックスイン的に使って良いのかは、未だ謎・・・。

昔どこかで（Java8が出た当初）　「interfaceのdefaultメソッドは、Listインターフェイスなど広く利用される標準ライブラリのインターフェイスにメソッド追加したいためだから、普通は使わない方が良い」的な文章を見た記憶が・・・。