java - 为什么 Java 8 类型推断在重载选择中不考虑 Lambda 抛出的异常?

标签 java exception lambda java-8 type-inference

我有一个关于 lambda 及其相关异常签名的 Java 8 推理的问题。

如果我定义一些方法 foo:

public static <T> void foo(Supplier<T> supplier) {
    //some logic
    ...
}

然后我得到了能够在大多数情况下为给定的 T 编写 foo(() -> getTheT()); 的简洁明了的语义。然而,在这个例子中,如果我的 getTheT 操作声明它 throws Exception,我的 foo 方法接受供应商不再编译:供应商get 的方法签名不会引发异常。

似乎一个不错的解决方法是重载 foo 以接受任一选项,重载定义为:

public static <T> void foo(ThrowingSupplier<T> supplier) {
   //same logic as other one
   ...
}

ThrowingSupplier 定义为

public interface ThrowingSupplier<T> {
   public T get() throws Exception;
}

通过这种方式,我们有一种供应商类型会引发异常,而另一种则不会。所需的语法是这样的:

foo(() -> operationWhichDoesntThrow()); //Doesn't throw, handled by Supplier
foo(() -> operationWhichThrows()); //Does throw, handled by ThrowingSupplier

但是,由于 lambda 类型不明确(可能无法在供应商和 ThrowingSupplier 之间解决),这会导致问题。做一个显式转换 la foo((ThrowingSupplier)(() -> operationWhichThrows())); 会起作用,但它消除了所需语法的大部分简洁性。

我想根本的问题是:如果 Java 编译器能够解决我的一个 lambda 表达式不兼容的事实,因为它在仅限供应商的情况下抛出异常,为什么它不能使用相同的在辅助类型推断案例中推导出 lambda 类型的信息?

任何人都可以向我指出的任何信息或资源同样将不胜感激,因为我不太确定在哪里可以找到有关此事的更多信息。

谢谢!

最佳答案

如果它让你感觉更好,这个话题在 JSR-335 设计过程中确实是经过仔细考虑的。

问题不是“为什么它不能”,而是“我们为什么选择不这样做”。当我们发现多个可能适用的重载时,我们当然可以选择在每组签名下推测性地归因于 lambda 主体,并修剪那些 lambda 主体未能对其进行类型检查的候选者。

但是,我们得出的结论是,这样做可能弊大于利;这意味着,例如,在此规则下,对方法主体的微小更改可能会导致某些方法重载选择决策在用户不打算这样做的情况下默默地更改。最后,我们得出结论,使用方法主体中存在错误来丢弃可能适用的候选者会导致更多的困惑而不是好处,特别是考虑到有一个简单且安全的解决方法——提供目标类型。我们认为这里的可靠性和可预测性超过了最佳简洁性。

关于java - 为什么 Java 8 类型推断在重载选择中不考虑 Lambda 抛出的异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32323081/

相关文章:

java - @RequestMapping ("/**") 不会加载js、css等静态内容

unit-testing - 如何测试 Clojure 中抛出的异常?

Scala函数: What is the return type

c++ - 这 4 个 lambda 表达式有什么区别?

java - 在一行中声明 Consumer 和操作

javascript - 在 ajax 调用中返回实体

java - 如何使用某些与 SQL 兼容的列类型在 Spring JPA 中存储 Joda 时间(任何)?

mysql - 如何在 MySQL 中执行异常处理(参照完整性)

While 循环中的 Java Switch-case 与同步块(synchronized block)导致循环中断

c# - RESTSharp 抛出时未捕获异常