orElseThrow 中的 lambda 表达式替代了什么?

what does the lambda expression in orElseThrow substitute?

让我们来看一些简单的示例代码:

Employee employee = repository.findById(id).orElseThrow(() -> new EmployeeNotFoundException(id));

根据我的理解,lambda 用于创建功能接口的子实例(只有 1 个方法),空括号表示该方法没有参数。

但是需要lambda的orElseThrow调用了什么方法呢?我认为它是 OptionalException

所以我的问题是:如果没有 lambda 调用,这会是什么样子?

员工class:

class Employee {

    private Long id;
    private String name;
    private String role;

    Employee() {}

    Employee(String name, String role) {
        this.name = name;
        this.role = role;
    }
}

和异常 class:

class EmployeeNotFoundException extends RuntimeException {

    EmployeeNotFoundException(Long id) {
        super("Could not find employee " + id);
    }
}

编辑:我对如何为 lambda- 表达式编写一个有效的、可编译的替换感兴趣。 现在我在

        Employee employee = repository.findById(id).orElseThrow(
            new Supplier<Throwable>() {
                @Override
                public Throwable get() {
                    return new EmployeeNotFoundException(id);
                }
            }
        )

但这是不对的,因为现在我需要将异常添加到方法签名中,而使用 lambda 时情况并非如此。这表明我不知道如何实现供应商 class 手动而不是通过使用 lambdas

跳过所有这些

通过查看 Optional 的 JavaDoc 可以很容易地回答这个问题:

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
    if (value != null) {
        return value;
    } else {
        throw exceptionSupplier.get();
    }
}

lambda 参数是提供要抛出的异常的供应商。

Optional::orElseThrow takes a Supplier 并且此 Supplier 用于获取 Exception 的实例,如果 Optional 下面具有 null 值,则将抛出该实例。查看 Optional::orElseThrow 的实现:

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
    if (this.value != null) {
        return this.value;
    } else {
        throw (Throwable)exceptionSupplier.get();
    }
}

它只是检查值是否存在,如果不存在 - 它从 Supplier 获取 Execption 实例并抛出它。

表达式:

() -> new EmployeeNotFoundException(id)

符合 Optional::orElseThrow 方法预期的 Supplier - Supplier<? extends X> exceptionSupplier 其中 X 扩展 Throwable。它甚至可以被提取到单独的变量中:

Supplier<EmployeeNotFoundException> execptionSupplier = () -> new EmployeeNotFoundException(id);
Employee employee = repository.findById(id).orElseThrow(execptionSupplier);

orElseThrowOptional class 的一种方法,具有以下签名:

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X

这意味着您传递的 lambda 表达式必须符合 Supplier<? extends X> 函数接口。 Supplier<T> 功能接口有一个不接受任何参数的方法和 returns T 的一个实例:T get().

您的 lambda 表达式 - () -> new EmployeeNotFoundException(id) - 匹配此功能接口。

此功能接口的 get() 方法用于创建 Throwable 实例,当 Optional 为空时 orElseThrow() 将抛出该实例。

我没有测试过,但我相信为了避免处理一般的 Throwable(通过在你的方法签名中声明它或在你的封闭方法中捕获它)你需要提供一个 Supplier<RuntimeException> 或类似的比 RuntimeException 更具体的类型。例如:

    Employee employee = repository.findById(id).orElseThrow(
        new Supplier<EmployeeNotFoundException>() {
            @Override
            public EmployeeNotFoundException get() {
                return new EmployeeNotFoundException(id);
            }
        }
    );

由于 EmployeeNotFoundException 是一个 RuntimeException,因此是一个未经检查的异常,编译器不会关心你是否处理异常。

您已经正确阅读了文档,至少部分正确:我们传递给 orElseThrow 的是一个 Supplier,所以这是 lambda 实现的接口,也是我们需要实现的接口无论我们是传递一个方法引用、一个 lambda 还是像您的情况那样传递一个用 new 实例化的对象。 Supplier 是采用一种类型参数的通用接口。文档还告诉我们 orElseThrow 需要 Supplier<? extends X>,其中 X extends Throwable,即 XThrowable 的子类型。所以传递 Supplier<Throwable> 并没有错,但是正如你所经历的,它给你带来了一些麻烦。我们被允许放入 Throwable 的任何子类型。输入 RuntimeExceptionEmployeeNotFoundException 可以让我们摆脱麻烦。

不过,当 lambda 运行良好时,我不知道你为什么要这么罗嗦。

Link: Documentation of the Supplier interface