13

我构建了一些我不太了解的东西——我不知道它是如何工作的。我已经熟悉了这篇multicatch 解释文章

考虑这两个异常和代码:

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/2to Exception(这就是我理解文档的方式)。

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

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

  • 是否选择最接近的超类作为 的类型e这个问题询问它,这就是答案。如果是这样,那么当我访问 e 时,为什么接口 CustomValueGetter “可见”?就我而言,它不应该e是一个Exception.

  • 如果不是,如果真正的类是MyException1或者MyException2为什么我不能简单地调用对这两个类可用的相同方法?

  • e一个动态生成的类的实例,它实现了两个异常的所有公共接口并且是最接近的公共超类类型?

4

3 回答 3

4

在第一个代码示例中,Java 无法自动推断,MyException1并且MyException2实现了函数getCustomValuee两者的类型层次结构中最大的共同点也是如此。也就是Exception没有这个功能getCustomValue。因此,它不起作用。

Java 是强类型的,尽管函数的命名相似,但它们与在同一类型中声明的函数不同。

在第二个代码中,两个异常都实现了CustomValueGetter。因此,是实现e的最大公分母。CustomValueGettergetCustomValue

于 2015-08-27T06:15:10.893 回答
4

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

将您的示例更改为此代码将能够再次编译。

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并因此能够使用它的功能。

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

答案中链接的引用:

更改异常类型的处理方式会以两种方式影响类型系统:除了对所有类型执行通常的类型检查外,异常类型还要进行额外的编译时分析。出于类型检查的目的,使用析取语句声明的 catch 参数具有类型 lub(t1, t2, ...) (JLSv3 §15.12.2.7),其中 ti 是声明 catch 子句要处理的异常类型。非正式地, lub(最小上限)是所讨论类型中最具体的超类型。在多捕获异常参数的情况下,所讨论的类型的最小上限始终存在,因为所有捕获的异常的类型都必须是 Throwable 的子类。因此, Throwable 是所讨论类型的上限,但它可能不是最小的上限,因为 Throwable 的某些子类可能是所讨论类型的超类(因此也是超类型),并且所讨论的异常类型可能实现公共接口。(lub 可以是超类和一个或多个接口的交集类型。)为了进行异常检查(JLSv3 §11.2),重新抛出最终或有效最终 catch 参数的 throw 语句(JLSv3 §11.2.2)是被视为精确地抛出那些异常类型:

于 2015-08-27T06:25:19.927 回答
2

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

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

文档摘录:

计算交集比人们可能最初意识到的要复杂得多。鉴于类型参数被限制为泛型类型的两个不同调用的超类型,例如 List 和 List,简单的交集操作可能会产生 Object。但是,更复杂的分析会产生一个包含 List 的集合。类似地,如果类型参数 T 被约束为两个不相关的接口 I 和 J 的超类型,我们可能会推断 T 必须是 Object,或者我们可能会获得 I & J 的更严格的界限。这些问题将在后面更详细地讨论本节。

简而言之,它是所有'd 异常(在我的例子中是 an )class中最接近的公共超类型,它实现了所有异常所做的所有接口(在我的例子中是and )。|ExceptionCustomValueGetterSerializable

于 2015-08-27T06:46:41.257 回答