Java
java8

JavaのOptionalとガード節を共存させてみた

Java SE 8のOptionalはガード節と相性が悪いのが悩みどころですが、
何とかガード節と共存できないかなと考えて作ってみました。

package com.ikemo3;

import java.util.Optional;

public class Main {

    public static void main(String[] args) {
        System.out.println(Main.sqrt("16"));
        System.out.println(Main.sqrt("-16"));
        System.out.println(Main.sqrt("str"));
    }

    public static String sqrt(String str) {
        return Optionals.of(str, String.class)
                .guard((s) -> toInteger(s), () -> "整数ではない")
                .guard((i) -> sqrt(i), () -> "正の数ではない")
                .get((d) -> String.valueOf(d));
    }

    /**
     * 値が正なら整数にする
     */
    public static Optional<Integer> toInteger(String str) {
        try {
            return Optional.of(Integer.valueOf(str));
        } catch (NumberFormatException e) {
            return Optional.empty();
        }
    }

    /**
     * 値が正なら平方根を取る
     */
    public static Optional<Double> sqrt(int i) {
        if (i >= 0) {
            return Optional.of(Math.sqrt(i));
        } else {
            return Optional.empty();
        }
    }
}

出力結果は以下のようになります。

4.0
正の数ではない
整数ではない

呼び出し方はこんな感じです。
だいたいガード節っぽく見えるかなと。

    public static String sqrt(String str) {
        return Optionals.of(str, String.class)
                .guard((s) -> toInteger(s), () -> "整数ではない")
                .guard((i) -> sqrt(i), () -> "正の数ではない")
                .get((d) -> String.valueOf(d));
    }

クラスの実装は次の通り。

package com.ikemo3;

import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;

public class Optionals<IN, RET> {
    private final IN value;
    private final RET elseValue;
    private final Class<RET> clazz;

    public static <IN, OUT> Optionals<IN, OUT> of(IN value, Class<OUT> clazz) {
        return new Optionals(value, clazz);
    }

    private Optionals(IN value, Class<RET> clazz) {
        this.value = value;
        this.clazz = clazz;
        this.elseValue = null;
    }

    private Optionals(RET elseValue) {
        this.value = null;
        this.clazz = null;
        this.elseValue = elseValue;
    }

    public <OUT> Optionals<OUT, RET> guard(Function<IN, Optional<OUT>> func, Supplier<RET> elseValue) {
        if (this.elseValue != null) {
            return new Optionals(this.elseValue);
        }

        Optional<OUT> result = func.apply(this.value);
        if (result.isPresent()) {
            return Optionals.of(result.get(), this.clazz);
        } else {
            return new Optionals(elseValue.get());
        }
    }

    public RET get(Function<IN, RET> func) {
        if (this.elseValue != null) {
            return this.elseValue;
        } else {
            return func.apply(this.value);
        }
    }
}