如何在 Lambda 内部抛出异常?

huangapple go评论65阅读模式
英文:

How to throw an exception inside a Lambda?

问题

在lambda内部如何抛出异常?例如在Optional的ifPresentOrElse中?

try {
   foo.bar(baz).ifPresentOrElse(theBar -> { 
      // 此处的代码需要抛出自定义异常,以便外部可以处理它
      // 目前无法直接抛出异常,并且这是一个未处理的异常
   }, () -> { response.set(notFound()); }
} catch (CustomException e) {
  在抛出异常时执行重要操作();
}
英文:

How do you throw an exception inside a lambda e.g in ifPresentOrElse of a Optional?

try {
   foo.bar(baz).ifPresentOrElse(theBar -> { 
      // A code inside here needs to throw a custom exception so the outside can handle it
      // It can't throw atm, and it is an unhandled exception here
   }, () -> { response.set(notFound()); }
} catch(CustomException e) {
  somethingImportantWhenExceptionIsThrown();
}

答案1

得分: 3

虽然你的问题中没有明确提到,但我会假设你打算从 ifPresentOrElse 中抛出一个已检查异常。

首先,你应该尝试理解为什么不能抛出异常。Consumer 接口和 Runnable 一样,只有一个单独的抽象方法,因此它是一个 @FunctionalInterface。从文档中可以看到:

> 注意,函数式接口的实例可以使用 lambda 表达式、方法引用或构造函数引用来创建。

这是 Consumer 接口的简化版本:

public interface Consumer<T> {

    void accept(T t); // &lt;-- 不会抛出异常
}

或者,你仍然可以使用旧的匿名内部类:

Consumer<Object> consumer = new Consumer<>() {
    @Override
    public void accept(Object o) { // &lt;-- 不会抛出异常
       // 处理
    }
};

因此,你不能accept 方法中抛出已检查异常。这是设计上不可能的。然而,你可以选择以下几种选项:

  • 使用自定义的 ThrowingConsumerThrowingOptional 类,它们可以抛出异常(因为你在方法签名中声明了异常),但不建议这样做。
  • 将已检查异常包装在 RuntimeException 中,后者无需在方法签名中声明,因此可以抛出。
  • 重写代码以不使用 lambda 表达式。

例如,针对最后一点:

Optional<Bar> barOpt = foo.bar(baz);
if(bar.isPresent()) {
    Bar bar = barOpt.get();
    try {
        // 抛出已检查异常的代码
    } catch(CustomException e) {
        当异常被抛出时的重要操作();
    }
} else {
    response.set(notFound());
}
英文:

Altough not explicitly mentioned in your question, i am going to assume that you intent to throw a checked exception from ifPresentOrElse.

First, you should try to understand why you cannot throw an exception. The Consumer interface has, as Runnable, only a single abstract method, which makes it a @FunctionalInterface. From the documentation:

> Note that instances of functional interfaces can be created with
> lambda expressions, method references, or constructor references.

This is a reduced version of the Consumer interface:

public interface Consumer&lt;T&gt; {

    void accept(T t); // &lt;-- NO throws Exception
}

Alternatively, you can still use the old anonymous inner class:

Consumer&lt;Object&gt; consumer = new Consumer&lt;&gt;() {
    @Override
    public void accept(Object o) { // &lt;-- NO throws Exception
       // consume
    }
};

Therefore, you cannot throw a checked exception from the accept method. It is not possible by design. You can, however, chose one of the following options:

  • Use a custom ThrowingConsumer and ThrowingOptional class, which can throw exceptions (since you declare them in their method signatures), not recommended.
  • Wrap the checked exception in a RuntimeException, which does not need to be declared in the method signature, and can therefore be thrown.
  • Rewrite your code to not use the lambda expression.

Example, for the last point:

Optional&lt;Bar&gt; barOpt = foo.bar(baz);
if(bar.isPresent()) {
    Bar bar = barOpt.get();
    try {
        // code that throws checked exception
    } catch(CustomException e) {
        somethingImportantWhenExceptionIsThrown();
    }
} else {
    response.set(notFound());
} 

答案2

得分: 0

这个问题的解决方法是使用Project Lombok

并且,将包装这个函数的方法用SneakyThrows进行注释,如下所示:

@SneakyThrows
void method() {
   try {
       foo.bar(baz).ifPresentOrElse(theBar -> { 
          throw()
       }, () -> { response.set(notFound()); }
   } catch(CustomException e) {
      somethingImportantWhenExceptionIsThrown();
   }
}
英文:

The solution for this is to use Project Lombok:

And have the method wrapping this function annotated with SneakyThrows, as such:

@SneakyThrows
void method() {
   try {
       foo.bar(baz).ifPresentOrElse(theBar -&gt; { 
          throw()
       }, () -&gt; { response.set(notFound()); }
   } catch(CustomException e) {
      somethingImportantWhenExceptionIsThrown();
   }
}

答案3

得分: 0

你可以使用Apache Commons Lang3库进行操作。

https://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/function/Failable.html

Optional.of(obj).ifPresentOrElse(Failable.asConsumer(theBar -> {
    throw new Exception();
}), () -> {});
英文:

You can do it with apache commons-lang3 library.

https://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/function/Failable.html

Optional.of(obj).ifPresentOrElse(Failable.asConsumer(theBar -&gt; {
    throw new Exception();
}), () -&gt; {});

huangapple
  • 本文由 发表于 2020年10月21日 02:06:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/64451002.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定