简介

最近发现许多小伙伴还不知道怎么在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,如下所示:

@FunctionalInterface
public 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表达式中优雅的进行反常转化的比如了。咱们运用的过程中一定要注意最终对反常的捕获。

好了,本文的代码:

本文的比如github.com/ddean2009/l…

更多文章请看 www.flydean.com