JavaでIf式をしてみる

## まえがき

本稿は、こちらの記事に影響を受けて衝動的に作られました。
[4歳娘「パパ、constしか使わないで？」](https://qiita.com/Yametaro/items/17f5a0434afa9b88c3b1)

## 本文

みなさん、Javaは好きですか。

私は最近まであまり好きではありませんでしたが、最近は結構好きです。

なんだかんだライブラリも豊富ですし、モダンな構文やsyntax sugarも増えてきました。

ただ、If式が無いのはいただけないので、それっぽいものを作ってみました。

## 完成品

```java
public class Iff<T, R> {
  Map<Predicate<T>, Supplier<R>> evaluations = new LinkedHashMap<>();
  private Supplier<R> elsef;

  public Iff(Predicate<T> condition, Supplier<R> then) {
    evaluations.put(condition, then);
  }

  public Iff<T, R> elseIf(Predicate<T> condition, Supplier<R> then) {
    evaluations.put(condition, then);
    return this;
  }

  public Iff<T, R> elsef(Supplier<R> elsef) {
    this.elsef = elsef;
    return this;
  }

  public Optional<R> eval(T target) {
    for (Map.Entry<Predicate<T>, Supplier<R>> entry : this.evaluations.entrySet()) {
      if (entry.getKey().test(target)) {
        var value = entry.getValue().get();
        return Objects.isNull(value) ? Optional.empty() : Optional.of(value);
      }
    }
    if (this.elsef == null) {
      return Optional.empty();
    } else {
      var result = this.elsef.get();
      if (Objects.isNull(result)) {
        return Optional.empty();
      }
      return Optional.of(result);
    }
  }
}
```

Tのパラメータを引数にとりチェックし、`Optional<R>`な値を返すクラスです（本当にこれはif式・・・？）

## 使い方

雑にテストとしてサンプルを書いてみました。

```java

class IffTest {

  @Test
  public void testIf() {
    // 文字列を評価してIntegerを返す
    var result = new Iff<String, Integer>((str) -> str.startsWith("hoge"), () -> 100).eval("hogeFuga");
    Assertions.assertEquals(result.get(), 100);
  }

  @Test
  public void testIfElseFirstMatch() {
    // elseIfメソッドで、elseIfを何度でも足せます。
    var result = new Iff<String, Integer>((str) -> str.startsWith("hoge"), () -> 100)
      .elseIf((str) -> str.startsWith("hogeFuga"), () -> 200)
      .elseIf((str) -> str.startsWith("hoho"), () -> 300)
      .eval("hogeFuga");
    Assertions.assertEquals(result.get(), 100);
  }

  @Test
  public void noMatch() {
    var result = new Iff<String, Integer>((str) -> str.startsWith("hoge"), () -> 100)
      .elseIf((str) -> str.startsWith("hogeFuga"), () -> 200)
      .elseIf((str) -> str.startsWith("hoho"), () -> 300)
      // elsefメソッドで、elseの場合の挙動をセットできます
      .elsef(() -> 400)
      .eval(UUID.randomUUID().toString());
    Assertions.assertEquals(result.get(), 400);
  }

  @Test
  public void noMatchNotExistsElse() {
    var result = new Iff<String, Integer>((str) -> str.startsWith("hoge"), () -> 100)
      .elseIf((str) -> str.startsWith("hogeFuga"), () -> 200)
      .elseIf((str) -> str.startsWith("hoho"), () -> 300)
      .eval(UUID.randomUUID().toString());
    // どの式にもマッチしない場合、Emptyが返却されます
    result.ifPresentOrElse(v -> {
      Assertions.fail();
    }, () -> {
      // elseだったら正解
    });
  }

  @Test
  public void elseResultIsNull() {
    var result = new Iff<String, Integer>((str) -> str.startsWith("hoge"), () -> 100)
      .elseIf((str) -> str.startsWith("hogeFuga"), () -> 200)
      .elseIf((str) -> str.startsWith("hoho"), () -> 300)
      .elsef(() -> null)
      .eval(UUID.randomUUID().toString());
    Assertions.assertTrue(result.isEmpty());
  }

  @Test
  public void elseIfResultIsNull() {
    var result = new Iff<String, Integer>((str) -> str.startsWith("hoge"), () -> 100)
      .elseIf((str) -> str.startsWith("mogemoge"), () -> null)
      .elseIf((str) -> str.startsWith("hoho"), () -> 300)
      .elsef(() -> null)
      .eval("mogemoge");
    Assertions.assertTrue(result.isEmpty());
  }
}
```