原始和参数化之间的区别 调用 orElseGet(Supplier) 时可选

Difference between raw and parameterized Optional when invoking orElseGet(Supplier)

提问人:Sy Pham 提问时间:10/1/2023 更新时间:10/2/2023 访问量:77

问:

请考虑以下代码:

public class Main {
    public static void main(String[] args)
             // throws Exception // line 1 
    {
        Optional empty1 = Optional.empty(); // line 2
        Optional<Object> empty2 = Optional.empty(); // line 3

        Optional.empty().orElseThrow(() -> new Exception("Empty optional")); // line 4
        empty1.orElseThrow(() -> new Exception("Empty optional")); // line 5
        empty2.orElseThrow(() -> new Exception("Empty optional")); // line 6
    }

}
  • 当第 1 行被注释时,第 4、5、6 行都会报告编译时错误:

    • 第 4 行和第 6 行:未处理的异常:java.lang.Exception
    • 第 5 行:未处理的异常:java.lang.Throwable 这让我觉得第 4 行是用类型参数调用的,就像第 6 行一样,但是什么让抛出的确切异常存在差异?<Object>
  • 当第 1 行取消注释时,只有第 5 行报告编译时错误:未处理的异常:java.lang.Throwable。如果我将第 1 行更改为投掷投掷,它将得到解决。为什么抛出 Exception 不足以让第 5 行解决编译时错误?

Java 异常 optional-parameters optional-chaining

答:

2赞 marcinj 10/2/2023 #1

区别是由于在 Java 中使用了原始类型。orElseThrow 的方法签名为:

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

因此,对于第 2 行和第 5 行的情况,您有:

Optional empty1 = Optional.empty(); // line 2
empty1.orElseThrow(() -> new Exception("Empty optional")); // line 5

在上面的签名情况下,编译器没有提供类型,即使认为它有,它也会生成原始调用。它将用其上限替换 X,即 Throwable,而 T 将是 Object。结果是:orElseThrowTX

public Object orElseThrow(Supplier exceptionSupplier) throws Throwable
                                                             ^^^^^^^^^

因此,编译器报告:

error: unreported exception Throwable

案例:

Optional.empty().orElseThrow(() -> new Exception("Empty optional")); // line 4

更有趣的是,这里没有指定显式类型,但编译器报告被抛出的错误。所以它与前一种情况不同。您必须再次查看所用函数的签名,在本例中为 Optional.empty():Exception

public static<T> Optional<T> empty()
             ^^^ ~~~ type inference

重要的部分是表明它将从封闭上下文推断其类型。在编译器中无法推断类型,而是使用类型。我相信 JLS 是这样说的:<T>Tline 4Object

https://docs.oracle.com/javase/specs/jls/se10/html/jls-18.html#jls-18.1.3

如果 Pl 没有 TypeBound,则绑定的 αl <: Object 将显示在集合中。

在 Oracle 教程中:

https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html

编译器需要类型参数 T 的值,因此它启动 值为 Object。因此,调用 Collections.emptyList 返回 List<Object 类型的值>

重要的是,您不是像第 2 行和第 5 行那样处理原始类型,而是处理泛型类型(泛型类型是 ),因此当调用 orElseThrow 时,它的签名是line 4TObject

public Object orElseThrow(Supplier exceptionSupplier) throws Exception

两种泛型类型 NAD 都是已知的,因此会生成泛型函数调用,这最终会导致您的示例中出现以下编译器错误:TX

error: 未上报异常 Exception;

现在,第 3 行和第 6 行显式使用泛型类型 Object,这使得 orElseThrow 的签名如前所述:

public Object orElseThrow(Supplier exceptionSupplier) throws Exception

这给出了相同的错误:

error: 未上报异常 Exception;