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

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

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);
      };
   }
   /**
    * ThrowableConsumer 生成.
    * @param consumer 例外スローする Consumer<T>処理
    * @param onCatch Exception捕捉処理
    * @return Consumer<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);
         }
      };
   }
   /**
    * ThrowableConsumer 生成(外に例外スロー).
    * @param consumer 例外スローする Consumer<T>処理
    * @return Consumer<T>
    */
   public static <T> Consumer<T> of(ThrowableConsumer<T> consumer){
      return t->{
         try{
            consumer.accept(t);
         }catch(Throwable ex){
            throw new RuntimeException(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;
   }
   /**
    * ThrowableFunction 生成.
    * @param function 例外スローする Function<T, R>処理
    * @param onCatch Exception捕捉処理 , R を返すもしくは、null を返さなければならない。
    * @return Function<T, R>
    */
   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);
         }
      };
   }
   /**
    * ThrowableFunction 生成(外に例外スロー).
    * @param function 例外スローする Function<T, R>処理
    * @return Function<T, R>
    */
   public static <T, R> Function<T, R> of(ThrowableFunction<T, R> function){
      return  t->{
         try{
            return function.apply(t);
         }catch(Throwable ex){
            throw new RuntimeException(ex);
         }
      };
   }
}
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);
   }
   /**
    * ThrowablePredicate 生成.
    * @param p 例外スローする Predicate<T>処理
    * @param onCatch Exception捕捉処理 , boolean値を返さなければならない。
    * @return Predicate<T>
    */
   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);
         }
      };
   }
   /**
    * ThrowablePredicate 生成(外に例外スロー).
    * @param p 例外スローする Predicate<T>処理
    * @return Predicate<T>
    */
   public static <T> Predicate<T> of(ThrowablePredicate<T> p){
      return t->{
         try{
            return p.test(t);
         }catch(Throwable ex){
            throw new RuntimeException(ex);
         }
      };
   }
}

これを使うものとして、
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)->{}));