Function<T, R> の andThen , compose は、
以前から、最初の apply 結果が null だったら、
NullPointerException になってしまう不満がありました。
よくありがちなデータ構造
import lombok.Data; @Data public class Bucket{ private Drink drink; }
import lombok.Data; @Data public class Drink{ private String name; }
の時、、
Function<Bucket, Drink> f1 = Bucket::getDrink; Function<Drink, String> f2 = Drink::getName;
Bucketのインスタンス bucket に対して andThenを使えば、
String name = f1.andThen(f2).apply(bucket);
compose を使うなら、
String name = f2.compose(f1).apply(bucket);
であるが、Bucket の Drink が null であれば、NullPointerException になってしまう。
という不満です。
単に、Function#apply の連結実行なら、下記のようなメソッドでも良いが、
これはあまりにもセンスがない
static <T,V,R> R getChain(T t,Function<T,V> before, Function<V,R> after) { return Optional.ofNullable(before.apply(t)).map(after::apply).orElse(null); }
そこで考えました。
Function<T, R> を拡張して andThen , compose をオーバーライドして、
最初の apply 結果が、null の時の対処をしてあげます。
以下、
・andThen , compose オーバーライドでは、最初の apply 結果=null なら、
null を返す Function ラムダ式を返す。
・引数追加の andThen , compose は、最初の apply 結果=null なら、
最終的に求めたい型の値を指定するか、Supplier で取得させるように指定します。
import java.util.Objects; import java.util.Optional; import java.util.function.Function; import java.util.function.Supplier; /** * NullableFunction */ public interface NullableFunction<T, R> extends Function<T, R>{ @Override default <V> Function<T, V> andThen(Function<? super R, ? extends V> after){ Objects.requireNonNull(after); return (T t) -> Optional.ofNullable(apply(t)).map(after::apply).orElse(null); } default <V> Function<T, V> andThen(Function<? super R, V> after, V v){ Objects.requireNonNull(after); return (T t) -> Optional.ofNullable(apply(t)).map(after::apply).orElse(v); } default <V> Function<T, V> andThen(Function<? super R, V> after, Supplier<V> sup){ Objects.requireNonNull(after); return (T t) -> Optional.ofNullable(apply(t)).map(after::apply).orElse(sup.get()); } @Override default <V> Function<V, R> compose(Function<? super V, ? extends T> before){ Objects.requireNonNull(before); return (V v) -> Optional.ofNullable(before.apply(v)).map(e->apply(e)).orElse(null); } default <V> Function<V, R> compose(Function<? super V, ? extends T> before, R r){ Objects.requireNonNull(before); return (V v) -> Optional.ofNullable(before.apply(v)).map(e->apply(e)).orElse(r); } default <V> Function<V, R> compose(Function<? super V, ? extends T> before, Supplier<R> sup){ Objects.requireNonNull(before); return (V v) -> Optional.ofNullable(before.apply(v)).map(e->apply(e)).orElse(sup.get()); } static <T,V,R> Function<T, R> bind(Function<T,V> before, Function<V,R> after){ return (T t) -> Optional.ofNullable(before.apply(t)).map(after::apply).orElse(null); } static <T,V,R> Function<T, R> bind(Function<T,V> before, Function<V,R> after, R r){ return (T t) -> Optional.ofNullable(before.apply(t)).map(after::apply).orElse(r); } static <T,V,R> Function<T, R> bind(Function<T,V> before, Function<V,R> after, Supplier<R> sup){ return (T t) -> Optional.ofNullable(before.apply(t)).map(after::apply).orElse(sup.get()); } }
andThen , compose のメソッドが増えてしまいましたが、いろんな場面を考慮すると便利です。
さらに、2つの Functionを指定して実行させる Function をstatic メソッドとして提供します。
サンプル
NullableFunction<Bucket, Drink> f1 = Bucket::getDrink;
String drinkName = f1.andThen(Drink::getName, ()->"Drink name is null").apply(bucket);
となります。