英文:
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); // <-- 不会抛出异常
}
或者,你仍然可以使用旧的匿名内部类:
Consumer<Object> consumer = new Consumer<>() {
    @Override
    public void accept(Object o) { // <-- 不会抛出异常
       // 处理
    }
};
因此,你不能从 accept 方法中抛出已检查异常。这是设计上不可能的。然而,你可以选择以下几种选项:
- 使用自定义的 
ThrowingConsumer和ThrowingOptional类,它们可以抛出异常(因为你在方法签名中声明了异常),但不建议这样做。 - 将已检查异常包装在 
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<T> {
    void accept(T t); // <-- NO throws Exception
}
Alternatively, you can still use the old anonymous inner class:
Consumer<Object> consumer = new Consumer<>() {
    @Override
    public void accept(Object o) { // <-- 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 
ThrowingConsumerandThrowingOptionalclass, 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<Bar> 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 -> { 
          throw()
       }, () -> { response.set(notFound()); }
   } catch(CustomException e) {
      somethingImportantWhenExceptionIsThrown();
   }
}
答案3
得分: 0
你可以使用Apache Commons Lang3库进行操作。
Optional.of(obj).ifPresentOrElse(Failable.asConsumer(theBar -> {
    throw new Exception();
}), () -> {});
英文:
You can do it with apache commons-lang3 library.
Optional.of(obj).ifPresentOrElse(Failable.asConsumer(theBar -> {
    throw new Exception();
}), () -> {});
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论