如何优雅的处理Lambda中异常?

在这里插入图片描述

1.如何优雅的处理Lambda中异常?

java8新增的特性Lambda表达式为我们使用匿名内部类提供了非常简洁的方式。然而,平时在使用lambda的过程发,却发现lambda表达式中如果存在异常,使用try-catch去处理异常,那么这个lambda表达式就会看起来非常臃肿。

在lambda中使用try-catch处理异常:

     List<String> list = Arrays.asList("海", "上", "月", "是", "天", "上", "月");

     list.forEach(e -> {
         try{
             Integer integer = Integer.valueOf(e);
         }catch (Exception ex){
             System.err.println("发生异常  :"+ex.toString());
         }
            
     });

这么做其实也没有什么问题。只不过在视觉上看着非常臃肿,破坏了lambda简洁的效果。除了使用try-catch的方式去处理异常,我们还可以选择throw的方式将异常抛出去。

但是实际上lambda对受检异常和非受检异常的上抛处理方式支持是不同的。

		List<String> list = Arrays.asList("海", "上", "月", "是", "天", "上", "月");
		// 非受检异常  编译通过
        list.forEach(e-> {
            throw  new RuntimeException();
        });
		// 受检异常 编译不通过
        list.forEach(e -> {
            throw  new IOException();
        });

可以看到,受检异常是不能抛出去的,编译无法通过。我们就只能使用try-catch或者将异常抛出去从而处理这个异常。

如果我们将异常从方法上再次抛出去呢?

@Test
public void test02() throws  IOException{

    List<String> list = Arrays.asList("海", "上", "月", "是", "天", "上", "月");
    //编译不通过
    list.forEach(e -> {
        throw  new IOException();
    });


}

很难过的是,对于受检异常,这样的做法也是不被允许的。

那么该如何简洁又优雅的处理lambda中的异常呢?

对于非受检异常,使用包装方法来进行处理:

List<String> list = Arrays.asList("海", "上", "月", "是", "天", "上", "月");

list.forEach(
        HandlingConsumerWrapper.handlingConsumerWrapper(e->{
    System.out.println(Integer.valueOf(e));
}));


/**
 * @ClassName handlingConsumerWrapper
 * @Description 对lambda异常进行处理的包装类
 * @Date 2019-10-25 下午 6:04
 * @Version V1.0
 */
public class HandlingConsumerWrapper {

    private static final Logger logger = LoggerFactory.getLogger(HandlingConsumerWrapper.class);

    public static <T> Consumer<T> handlingConsumerWrapper(Consumer<T> throwingConsumer){

        return  i -> {

            try{

                throwingConsumer.accept(i);
            }
            catch (Exception e){

                logger.error(" 处理lambda中的异常发生异常 "+e.toString());

            }

        };
    }

}

简单来说,就是将本来要传入的Consumer接口的实现,传入handlingConsumerWrapper方法中,并对其中的异常进行处理,然后再返回一个Consumer接口的实现。

这样来处理,在lambda中既可以处理异常又可以保证代码的简洁。不会让lambda显得很臃肿。

对于受检异常,需要自己定义一个函数接口:

/**
 * @ClassName ThrowingConsumer
 * @Description 封装consumer接口异常的接口
 * @Author
 * @Date 2019-10-25 下午 6:02
 * @Version V1.0
 */
@FunctionalInterface
public interface ThrowingConsumer<T, E extends Exception> {
	//对异常选择上抛	原consumer接口没有上抛 才使得不好处理
    void accept (T t)throws Exception;
}

 List<String> list = Arrays.asList("海", "上", "月", "是", "天", "上", "月");

        list.forEach(
                HandlingConsumerWrapper.handlingConsumerWrapper(e->{
                    File file = new File("");
                    file.createNewFile();
                }));
}


public static <T,E extends Exception>  Consumer<T> handlingConsumerWrapper(ThrowingConsumer<T, E> throwingConsumer){

    return  i -> {

        try{
			//处理函数接口抛上来的异常
            throwingConsumer.accept(i);
          
        }catch (Exception e){

            logger.error(" 处理lambda中的异常发生异常 "+e.toString());

        }

    };
}

这里实际上是定义了一个函数接口ThrowingConsumer,ThrowingConsumer的方法和Consumer接口一致,只不过抛出了异常,这样我们才可以在包装方法中处理受检异常。

上述两种方法实际上都是将异常在包装方法中进行了处理。下面说一下一定要将异常抛出方法的情况:

对于一定想要将异常抛出方法的情况:

//可以将异常抛出来了
@Test
public void test_Consumer_with_checked_exceptions() throws IllegalAccessException, ClassNotFoundException {
    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
            .forEach(rethrowConsumer(className -> System.out.println(Class.forName(className))));

    Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
            .forEach(rethrowConsumer(System.out::println));
}

//定义可以抛出异常的函数接口
@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
    void accept(T t) throws E;
}


    public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) throws E {
        return t -> {
            try {
                consumer.accept(t);
            } catch (Exception exception) {
            	//将受查异常作为非受查异常抛出去
                throwAsUnchecked(exception);
            }
        };
    }

@SuppressWarnings("unchecked")
private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E {
    throw (E) exception;
}

可以看到,其实大体上和try-catch的处理是基本一致的。只不过在将异常catch之后,将异常全部作为非受检异常抛了出去。但是这种情况就可以将异常都上抛给调用的方法进行处理了。

处理lambda的工具类:

package com.uplus.gateway.util.lambda;

import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * @ClassName LambdaExceptionUtil
 * @Description
 * @Author
 * @Date 2019-10-27 下午 3:16
 * @Version V1.0
 */
public class LambdaExceptionUtil {

    @FunctionalInterface
    public interface Consumer_WithExceptions<T, E extends Exception> {
        void accept(T t) throws E;
    }

    @FunctionalInterface
    public interface BiConsumer_WithExceptions<T, U, E extends Exception> {
        void accept(T t, U u) throws E;
    }

    @FunctionalInterface
    public interface Function_WithExceptions<T, R, E extends Exception> {
        R apply(T t) throws E;
    }

    @FunctionalInterface
    public interface Supplier_WithExceptions<T, E extends Exception> {
        T get() throws E;
    }

    @FunctionalInterface
    public interface Runnable_WithExceptions<E extends Exception> {
        void run() throws E;
    }

    /**
     * .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name)))); or .forEach(rethrowConsumer(ClassNameUtil::println));
     */
    public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) throws E {
        return t -> {
            try {
                consumer.accept(t);
            } catch (Exception exception) {
                throwAsUnchecked(exception);
            }
        };
    }

    public static <T, U, E extends Exception> BiConsumer<T, U> rethrowBiConsumer(BiConsumer_WithExceptions<T, U, E> biConsumer) throws E {
        return (t, u) -> {
            try {
                biConsumer.accept(t, u);
            } catch (Exception exception) {
                throwAsUnchecked(exception);
            }
        };
    }

    /**
     * .map(rethrowFunction(name -> Class.forName(name))) or .map(rethrowFunction(Class::forName))
     */
    public static <T, R, E extends Exception> Function<T, R> rethrowFunction(Function_WithExceptions<T, R, E> function) throws E {
        return t -> {
            try {
                return function.apply(t);
            } catch (Exception exception) {
                throwAsUnchecked(exception);
                return null;
            }
        };
    }

    /**
     * rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))),
     */
    public static <T, E extends Exception> Supplier<T> rethrowSupplier(Supplier_WithExceptions<T, E> function) throws E {
        return () -> {
            try {
                return function.get();
            } catch (Exception exception) {
                throwAsUnchecked(exception);
                return null;
            }
        };
    }

    /**
     * uncheck(() -> Class.forName("xxx"));
     */
    public static void uncheck(Runnable_WithExceptions t) {
        try {
            t.run();
        } catch (Exception exception) {
            throwAsUnchecked(exception);
        }
    }

    /**
     * uncheck(() -> Class.forName("xxx"));
     */
    public static <R, E extends Exception> R uncheck(Supplier_WithExceptions<R, E> supplier) {
        try {
            return supplier.get();
        } catch (Exception exception) {
            throwAsUnchecked(exception);
            return null;
        }
    }

    /**
     * uncheck(Class::forName, "xxx");
     */
    public static <T, R, E extends Exception> R uncheck(Function_WithExceptions<T, R, E> function, T t) {
        try {
            return function.apply(t);
        } catch (Exception exception) {
            throwAsUnchecked(exception);
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E {
        throw (E) exception;
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值