【问题标题】:Determining compile-time multicatch exception type确定编译时多捕获异常类型
【发布时间】:2015-11-21 09:26:56
【问题描述】:

我构建了一些我不太了解的东西 - 我不知道它是如何工作的。我已经熟悉了这个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/2 转换为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 的类型? This question asks about it 应该就是答案。如果是这样,那么当我访问 e 时,为什么接口 CustomValueGetter “可见”?在我的情况下,e 不应该是Exception

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

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

【问题讨论】:

    标签: java exception try-catch java-7 multi-catch


    【解决方案1】:

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

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

    来自答案中链接的引用:

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

    【讨论】:

    • 如果您的答案中发布的链接的接受答案是正确的,那么我使用的接口永远不应该在e 中解决,因为这两个异常的超类都没有实现它。
    • @Dariusz 我编辑并引用了另一个问题的答案中的链接,通知后面的部分说:A lub can be an intersection type of a superclass and one or more interfaces.
    • 太糟糕了,措辞没有进入实际的 Java 语言文档(写得非常晦涩)。我想最接近的是Intersection Types 的描述,其中提到了“类型推断(§15.12.2.7)”,catch clause 的描述也是如此。
    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

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

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

      文档摘录:

      计算交集比一开始可能更复杂 意识到。鉴于类型参数被限制为超类型 泛型类型的两个不同调用,比如 List 和 列表,天真的交集操作可能会产生对象。 然而,更复杂的分析会产生一组包含 列表。类似地,如果类型参数 T 被约束为 两个不相关的接口 I 和 J 的超类型,我们可以推断 T 必须 是对象,否则我们可能会获得 I & J 的更严格的界限。这些问题 本节稍后将详细讨论。

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

      【讨论】:

        猜你喜欢
        • 2010-10-08
        • 2011-01-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-12
        相关资源
        最近更新 更多