ラムダ式の例外処理を綺麗にする

ラムダ式の中で例外が発生する場合は厄介なもので、何とかしようとするならラップに近い形で自分で用意するしかないみたい。

Consumer 、Function 、Predicate これらの例外捕捉処理バージョンを作っておけばかなり便利かもしれません。

import java.io.Serializable;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
 * ThrowableConsumer
 */
@FunctionalInterface
public interface ThrowableConsumer<T> extends Serializable{

   void accept(T t) throws Exception;

   default Consumer<T> andThen(Consumer<? super T> after, BiConsumer<T, Exception> onCatch){
      Objects.requireNonNull(after);
      return (T t)->{
         try{
            accept(t);
         }catch(Exception e){
            onCatch.accept(t, e);
         }
         after.accept(t);
      };
   }
   public static <T> Consumer<T> of(ThrowableConsumer<T> consumer, BiConsumer<T, Exception> onCatch){
      return t->{
         try{
            consumer.accept(t);
         }catch(Exception ex){
            onCatch.accept(t, ex);
         }
      };
   }
}
import java.io.Serializable;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
 * ThrowableFunction
 */
@FunctionalInterface
public interface ThrowableFunction<T, R> extends Serializable{

   R apply(T t) throws Exception;

   default <V> Function<V, R> compose(Function<? super V, ? extends T> before, BiFunction<V, Exception, R> onCatch){
      Objects.requireNonNull(before);
      return (V v)->{
         try{
            return apply(before.apply(v));
         }catch(Exception e){
            return onCatch.apply(v, e);
         }
      };
   }

   default <V> Function<T, V> andThen(Function<? super R, ? extends V> after, BiFunction<T, Exception,  ? extends V> onCatch){
      Objects.requireNonNull(after);
      return (T t)->{
         try{
            return after.apply(apply(t));
         }catch(Exception e){
            return onCatch.apply(t, e);
         }
      };
   }

   static <T> Function<T, T> identity(){
      return t -> t;
   }

   public static <T, R> Function<T, R> of(ThrowableFunction<T, R> function, BiFunction<T, Exception, R> onCatch){
      return  t->{
         try{
            return function.apply(t);
         }catch(Exception e){
            return onCatch.apply(t, e);
         }
      };
   }
}
import java.io.Serializable;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Predicate;
/**
 * ThrowablePredicate
 */
@FunctionalInterface
public interface ThrowablePredicate<T> extends Serializable{

   boolean test(T t) throws Exception;

   default Predicate<T> and(Predicate<? super T> other){
      Objects.requireNonNull(other);
         return (t)->{
         try{
            return test(t) && other.test(t);
         }catch(Exception e){
            return false;
         }
      };
   }
   default Predicate<T> and(Predicate<? super T> other, BiFunction<T, Exception, Boolean> onCatch){
      Objects.requireNonNull(other);
         return (t)->{
         try{
            return test(t) && other.test(t);
         }catch(Exception e){
            return onCatch.apply(t, e);
         }
      };
   }

   default Predicate<T> negate(){
      return (t)->{
         try{
            return !test(t);
         }catch(Exception e){
            return false;
         }
      };
   }
   default Predicate<T> negate(BiFunction<T, Exception, Boolean> onCatch){
      return (t)->{
         try{
            return !test(t);
         }catch(Exception e){
            return !onCatch.apply(t, e);
         }
      };
   }

   default Predicate<T> or(Predicate<? super T> other){
      Objects.requireNonNull(other);
      return (t)->{
         try{
            return test(t) || other.test(t);
         }catch(Exception e){
            return false;
         }
      };
   }
   default Predicate<T> or(Predicate<? super T> other, BiFunction<T, Exception, Boolean> onCatch){
      Objects.requireNonNull(other);
      return (t)->{
         try{
            return test(t) || other.test(t);
         }catch(Exception e){
            return onCatch.apply(t, e);
         }
      };
   }

   static <T> Predicate<T> isEqual(Object targetRef){
      return (null==targetRef)  ? Objects::isNull : object -> targetRef.equals(object);
   }

   public static <T> Predicate<T> of(ThrowablePredicate<T> p, BiFunction<T, Exception, Boolean> onCatch){
      return t->{
         try{
            return p.test(t);
         }catch(Exception e) {
            return onCatch.apply(t, e);
         }
      };
   }
}

これを使うものとして、
Javaで MACアドレスを抽出する - Oboe吹きプログラマの黙示録
の処理を書いてみましょう。

最初の NetworkInterface.getNetworkInterfaces() だけは、SocketException の捕捉を外側(呼出し側)で処理を書かなくては
ならないですが、
NetworkInterface の isUp() や、getHardwareAddress() で発生の Exception 捕捉処理は
ThrowableXxxxx . of メソッドに、ラムダで書くことができます。
  特に、Predicate で、(t, error)->false と、問答無用に無視を指定できるのが良いです。

Collections.list(NetworkInterface.getNetworkInterfaces()).stream()
.filter(ThrowablePredicate.of(t->t.isUp(), (t, error)->false))
.forEach(ThrowableConsumer.of(nf->{
	String address = Optional.ofNullable(nf.getHardwareAddress())
			.map(e->IntStream.range(0, e.length).mapToObj(i->String.format("%02X", e[i])).collect(Collectors.joining(" "))
		).orElse(null);

        System.out.printf("%d,%d,%s,\"%s\",%s%n"
            , nf.isLoopback() ? 1 : 0
            , nf.isUp() ? 1 : 0
            , nf.getName()
            , nf.getDisplayName()
            , address
        );
}, (e, error)->{}));