Java 中涉及 throws 子句的类型推断解决过程

Type Inference resolution procedure involving throws clause in Java

考虑 JLS §18.1.3 中的以下文章 - 边界

在这里,当我们尝试确定推理变量的边界集时 - 我们会遇到以下情况之一:

...

  • throws α: The inference variable α appears in a throws clause.

...

A bound of the form throws α is purely informational: it directs resolution to optimize the instantiation of α so that, if possible, it is not a checked exception type.

我认为这种说法是错误的:

我的理解是正确的还是我遗漏了什么?

我认为你的interpretation/understanding这个说法有点误导:

A bound of the form throws α is purely informational: it directs resolution to optimize the instantiation of α so that, if possible, it is not a checked exception type.

那一行指的是分辨率,据我了解,它不是关于 throws α 在哪里,而是关于 α 在哪里被推断,可以想象是方法的调用。

考虑这个 class:

static class MyClass {

    public static void main(String[] args) {
        MyClass.<RuntimeException>something(0); // same as MyClass.something(1);

        try {
            MyClass.<IOException>something(2);
        } catch (IOException ex) {
            // checked exception
        }
    }

    /**
     * Will throw IOException if argument is 2, a RuntimeException otherwise
     */
    static <T extends Exception> void something(int a) throws T {
        if (a == 2) {
            throw (T) new IOException(); //of course it's a bad cast
        } else {
            throw (T) new Exception();
        }
    }
}

分析完两个something方法,重点关注main方法中的调用:

调用 MyClass.<IOException>something(0) 需要一个 IOException。调用者知道它(假设完整记录合同而不是紧密耦合的代码),并处理异常。
这已经告诉你变量可以是一个检查异常,与你的想法相反。

相反,调用 MyClass.<RuntimeException>something(0) 期望基于类似原因出现运行时异常。

如何推断 α(上例中的 T)允许编译器跳过强制调用者 catch/handle 异常(如果要查看边界,否则必须)

现在关于“优化”:被限制为 extends Exception 的类型变量可以合理地预期解析为检查异常。但是,如果调用者知道它应该是一个运行时异常,它可以“通知”编译器它将是一个运行时异常。这就是我通过在类型见证中指定 RuntimeException 所做的(当没有明确给出类型参数时,RuntimeException 也是已解析的类型)。

我们可以花几天时间来解释“优化”,但至少我作为调用者不必 try/catch 调用,而且我仍然没有扰乱编译器(第一次调用)。

考虑这个例子:

public class Main {

    public static void main(String[] args) {
        foo(() -> System.out.println("Foo"));
    }

    public static <T extends Exception> void foo(ThrowingRunnable<T> runnable) throws T {
        runnable.run();
    }
}

interface ThrowingRunnable<T extends Exception> {
    void run() throws T;
}

在调用foo时类型参数T的推断过程中,会在类型变量T上绑定一个“throws”,而T是推断为RuntimeException。如果不是这个界限,T 会因为 T extends Exception 界限而被推断为 Exception。这意味着我需要做:

try {
    foo(() -> System.out.println("Foo"));
catch (Exception e) {
    // This won't be reached!
}

我不得不处理异常,即使我在 lambda 中所做的只是打印东西!这似乎不太好,是吗?

这个bound的目的是如果方法没有理由抛出checked exception,它就不会抛出checked exception,这样就不用写那么多try...到处都是。 foo 如果您执行以下操作会抛出检查异常:

foo(() -> new FileOutputStream("foo"));

如果绑定的效果是强制 T 成为一个检查异常,它根本就不会很有用。