确定编译时多捕获异常类型

Determining compile-time multicatch exception type

我构建了一些我并不真正理解的东西 - 我不知道它是如何工作的。我已经熟悉了这个 multicatch explaination article.

考虑这两个异常和代码:

public class MyException1 extends Exception {
  // constructors, etc
  String getCustomValue();
}

public class MyException2 extends Exception {
  // constructors, etc
  String getCustomValue() { return "foo"; }
}

try {
  //...
} catch (MyException1|MyException2 e) {
  e.getCustomValue(); // won't work, as I expected
}

我将无法调用 getCustomValue(),即使方法是相同的,因为在 Java 内部,上面的 try/catch 应该实际上是在转换 MyException1/2Exception (这就是我对文档的理解)。

但是,如果我引入这样的接口:

public interface CustomValueGetter {
  String getCustomValue();
}

public class MyException1 extends Exception implements CustomValueGetter /*...*/
public class MyException2 extends Exception implements CustomValueGetter /*...*/

并将其添加到两个异常中,Java实际上可以让我使用该方法。然后调用这个有效:

try {
  //...
} catch (MyException1|MyException2 e) {
  e.getCustomValue(); // does work
}

简而言之,我的问题是:这里实际发生了什么:(MyException1|MyException2 e)

什么是e

在第一个代码示例中 Java 无法自动推断,MyException1MyException2 都实现了函数 getCustomValue。所以 e 是两者类型层次结构中最大的共同点;即 Exception 并且没有函数 getCustomValue。因此,它不起作用。

Java 是强类型的,尽管函数的命名相似,但与以相同类型声明的函数不同。

在第二个代码中,两个异常都实现了 CustomValueGetter。因此,e 是实现 getCustomValue.

的最大公分母 CustomValueGetter

正如 Ischuetze 所说,e 正在寻找两个异常共享的 class 或接口。在您的第一个示例中,尽管存在异常 class,但仍找不到共享 class,因此它只能使用它提供的方法。

把你的例子改成这段代码就可以重新编译了。

public class MyException12 extends Exception {
    public String getCustomValue(){ return "boo"; };
}

public class MyException1 extends MyException12{
    public String getCustomValue() { return "foo"; };
}

public class MyException2 extends MyException12{
    // constructors, etc
    public String getCustomValue() { return "foo"; };
}

正如您在界面示例中一样,异常通知 MyException1MyException2 都具有 MyException12,因此能够使用它的功能。

Here 是一个 SO 问题,回答了整个问题以及 e 的类型。

答案中引用link:

Changing the handling of exception types affects the type system in two ways: in addition to the usual type checking performed on all types, exception types undergo an additional compile time analysis. For the purpose of type checking, a catch parameter declared with a disjunction has type lub(t1, t2, ...) (JLSv3 §15.12.2.7) where the ti are the exception types the catch clause is declared to handle. Informally, the lub (least upper bound) is the most specific supertype of the types in question. In the case of a multi-catch exception parameter, the least upper bound of the types in question always exists since the types of all the caught exceptions must be subclasses of Throwable. Therefore, Throwable is an upper bound of the types in question, but it may not be the least upper bound since some subclass of Throwable may be a superclass (and thereby also a supertype) of the types in question and the exception types in question may implement a common interface. (A lub can be an intersection type of a superclass and one or more interfaces.) For the purpose of exception checking (JLSv3 §11.2), a throw statement (JLSv3 §11.2.2) that rethrows a final or effectively final catch parameter is treated as throwing precisely those exception types that:

挖掘@Kevin Eshche 的 JLS link 我似乎找到了正确的答案。

在编译时e实际上是一个最小上限,定义在JLS-15.12.2.7.

文档摘录:

Computing the intersection is more complicated than one might first realize. Given that a type parameter is constrained to be a supertype of two distinct invocations of a generic type, say List and List, the naive intersection operation might yield Object. However, a more sophisticated analysis yields a set containing List. Similarly, if a type parameter T is constrained to be a supertype of two unrelated interfaces I and J, we might infer T must be Object, or we might obtain a tighter bound of I & J. These issues are discussed in more detail later in this section.

简而言之,它是所有 | 异常中最接近的公共超类型的 class(在我的例子中是 Exception),它实现了所有接口例外情况(在我的例子中,CustomValueGetterSerializable)。