简介

最近发现很多小伙伴还不晓得如何在lambda表达式中优雅的解决checked exception,所以明天就重点和大家来探讨一下这个问题。

lambda表达式自身是为了不便程序员书写不便的工具,应用lambda表达式能够让咱们的代码更加简洁。

可能大多数小伙伴在应用的过程中素来没有遇到过外面蕴含异样的状况,所以对这种在lambda表达式中异样的解决可能没什么教训。

不过没关系,明天咱们就来一起探讨一下。

lambda表达式中的checked exception

java中异样的类型,大家应该是耳熟能详了,具体而言能够有两类,一种是checked exception, 一种是unchecked exception。

所谓checked exception就是须要在代码中手动捕捉的异样。unchecked exception就是不须要手动捕捉的异样,比方运行时异样。

首先咱们定义一个checked exception,间接继承Exception就好了:

public class MyCheckedException extends Exception{    @java.io.Serial    private static final long serialVersionUID = -1574710658998033284L;    public MyCheckedException() {        super();    }    public MyCheckedException(String s) {        super(s);    }}

接下来咱们定义一个类,这个类中有两个办法,一个抛出checked exception,一个抛出unchecked exception:

public class MyStudents {    public int changeAgeWithCheckedException() throws MyCheckedException {        throw new MyCheckedException();    }    public int changeAgeWithUnCheckedException(){        throw new RuntimeException();    }}

好了,咱们首先在lambda表达式中抛出CheckedException:

    public static void streamWithCheckedException(){        Stream.of(new MyStudents()).map(s->s.changeAgeWithCheckedException()).toList();    }

这样写在现代化的IDE中是编译不过的,它会提醒你须要显示catch住CheckedException,所以咱们须要把下面的代码改成上面这种:

    public static void streamWithCheckedException(){        Stream.of(new MyStudents()).map(s-> {            try {                return s.changeAgeWithCheckedException();            } catch (MyCheckedException e) {                e.printStackTrace();            }        }).toList();    }

这样做是不是就能够了呢?

再思考一个状况,如果stream中不止一个map操作,而是多个map操作,每个map都抛出一个checkedException,那岂不是要这样写?

    public static void streamWithCheckedException(){        Stream.of(new MyStudents()).map(s-> {            try {                return s.changeAgeWithCheckedException();            } catch (MyCheckedException e) {                e.printStackTrace();            }        }).map(s-> {            try {                return s.changeAgeWithCheckedException();            } catch (MyCheckedException e) {                e.printStackTrace();            }        }).        toList();    }

切实是太难看了,也不不便书写,那么有没有什么好的办法来解决,lambda中的checked异样呢?方法当然是有的。

lambda中的unchecked exception

下面例子中咱们抛出了一个checked exception,那么就必须在lambda表达式中对异样进行捕获。

那么咱们可不可以换个思路来考虑一下?

比方,把下面的checked exception,换成unchecked exception会怎么样呢?

    public static void streamWithUncheckedException(){        Stream.of(new MyStudents()).map(MyStudents::changeAgeWithUnCheckedException).toList();    }

咱们能够看到程序能够失常编译通过,能够缩小或者简直不须要应用try和catch,这样看起来,代码是不是简洁很多。

那么咱们是不是能够思考把checked exception转换成为unchecked exception,而后用在lambda表达式中,这样就能够简化咱们的代码,给程序员以更好的代码可读性呢?

说干就干。

根本的思路就是把传入的checked exception转换为unchecked exception,那么怎么转换比拟适合呢?

这里咱们能够用到JDK中的类型推断,通过应用泛型来达到这样的目标:

    public static <T extends Exception,R> R sneakyThrow(Exception t) throws T {        throw (T) t;    }

这个办法接管一个checked exception,在外部强制转换之后,抛出T。

看看在代码中如何应用:

    public static void sneakyThrow(){            Stream.of(new MyStudents()).map(s -> SneakilyThrowException.sneakyThrow(new IOException())).toList();    }

代码能够编译通过,这阐明咱们曾经把checked异样转换成为unchecked异样了。

运行之后你能够失去上面的输入:

Exception in thread "main" java.io.IOException    at com.flydean.Main.lambda$sneakyThrow$1(Main.java:28)    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)    at java.base/java.util.stream.Streams$StreamBuilderImpl.forEachRemaining(Streams.java:411)    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575)    at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)    at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616)    at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:622)    at java.base/java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:627)    at com.flydean.Main.sneakyThrow(Main.java:28)    at com.flydean.Main.main(Main.java:9)

从日志中,咱们能够看出最初抛出的还是java.io.IOException,然而如果咱们尝试对这个异样进行捕捉:

    public static void sneakyThrow(){        try {            Stream.of(new MyStudents()).map(s -> SneakilyThrowException.sneakyThrow(new IOException())).toList();        }catch (IOException e){           System.out.println("get exception");        }    }

在编译器中会提醒编译不通过,因为代码并不会抛出IOException。如果你把IOException批改为RuntimeException,也没法捕捉到最初的异样。

只能这样批改:

    public static void sneakyThrow(){        try {            Stream.of(new MyStudents()).map(s -> SneakilyThrowException.sneakyThrow(new IOException())).toList();        }catch (Exception e){           System.out.println("get exception");        }    }

能力最终捕捉到stream中抛出的异样。所以如果你应用了我这里说的这种异样转换技巧,那就必须要特地留神这种异样的捕捉状况。

对lambda的最终革新

下面能够封装异样了是不是就实现了咱们的工作了呢?

并不是,因为咱们在map中传入的是一个Function而不是一个专门的异样类。所以咱们须要对Function进行额定的解决。

首先JDK中的Function中必须实现这样的办法:

    R apply(T t);

如果这个办法外面抛出了checked Exception,那么必须进行捕捉,如果不想捕捉的话,咱们能够在办法申明中抛出异样,所以咱们须要从新定义一个Function,如下所示:

@FunctionalInterfacepublic interface FunctionWithThrow<T, R> {    R apply(T t) throws Exception;}

而后再定义一个unchecked办法,用来对FunctionWithThrow进行封装,通过捕捉抛出的异样,再次调用sneakyThrow进行checked异样和unchecked异样的转换:

    static <T, R> Function<T, R> unchecked(FunctionWithThrow<T, R> f) {        return t -> {            try {                return f.apply(t);            } catch (Exception ex) {                return SneakilyThrowException.sneakyThrow(ex);            }        };    }

最初,咱们就能够在代码中优雅的应用了:

    public static void sneakyThrowFinal(){        try {            Stream.of(new MyStudents()).map(SneakilyThrowException.unchecked(MyStudents::changeAgeWithCheckedException)).toList();        }catch (Exception e){            System.out.println("get exception");        }    }

总结

以上就是如何在lambda表达式中优雅的进行异样转换的例子了。大家应用的过程中肯定要留神最初对异样的捕捉。

好了,本文的代码:

本文的例子https://github.com/ddean2009/learn-java-base-9-to-20/tree/master/lambda-and-checked-exception/

更多文章请看 www.flydean.com