【问题标题】:Why compilation depends on how exception is thrown within instance initializer?为什么编译取决于在实例初始化程序中如何引发异常?
【发布时间】:2018-07-16 15:49:53
【问题描述】:

为什么第一个代码 sn-p 编译失败(编译错误“初始化程序无法正常完成”)而第二个代码编译正常?唯一的区别在于抛出异常的方式!

class CompilationFailsClass {   

   {
      // c.ERR Initializer does not complete normally
      throw new IOException();   
   }

   public CompilationFailsClass() throws IOException {
        // constructor
   }
}

class CompilationOKClass {

   // with empty body effect is exactly same
   static void f() throws IOException { throw new IOException(); }

   {
      f(); // OK !
   }

   public CompilationOKClass() throws IOException {
   }
}

JLS §11.2.3:

如果实例变量初始化器是编译时错误 (第 8.3.2 节)或命名类的实例初始化程序(第 8.6 节)可以抛出 检查异常类,除非命名类至少有一个 显式声明的构造函数和异常类或其之一 超类在每个的 throws 子句中显式声明 构造函数。

【问题讨论】:

  • @Tom f() throws IOException { throw new IOException(); } 产生相同的效果(编译得很好!)。好的,我更正了这个问题,现在它已经 100% 清楚了。
  • 在第一个初始化块中有throw new IOException();。它不应该是某种方法的一部分吗?
  • 值得整理您的问题以一致且惯用地格式化所有代码。这将使整个事情更容易阅读。

标签: java initializer


【解决方案1】:

JLS 对此非常清楚,section 8.6

如果实例初始化程序无法正常完成,则为编译时错误(第 14.21 节)。

在您的第一个示例中,实例初始化程序无法正常完成。

在第二个例子中,就编译器而言可以正常完成。它永远不会,但编译器不知道,所以没关系。

【讨论】:

  • “如果一个命名类的实例变量初始化器(§8.3.2)或实例初始化器(§8.6)可以抛出一个检查异常类,这是一个编译时错误,除非命名类至少有一个显式声明的构造函数,并且异常类或其超类之一在每个构造函数的 throws 子句中显式声明。” - JLS 自相矛盾。几年前在 coderanch 上讨论过同样的问题:coderanch.com/t/323675/java/…
  • @Tom:我看不出哪里有矛盾。 “实例初始化程序可以抛出检查异常”和“编译器可以告诉实例初始化程序无法正常完成”之间存在很大区别。第一个在第二个示例中介绍 - 实例初始化程序调用 f,它被声明为可能抛出 IOException,但这没关系,因为构造函数也被声明为抛出 IOException
  • 那么它是误导性的,因为 OPs 第二个版本确实抛出了异常,只是包装在一个“代理方法”中。 JLS 中没有明确区分直接在初始化程序块中抛出的异常和在初始化程序块调用的方法中抛出的异常。在这两种情况下,该块都不会正常完成。
  • @Tom:我认为这根本没有误导性。您只需要了解“无法正常完成”的含义,这将在第 14 章中进行解释。“我们知道不会发生的事情”和“就编译器而言可证明不会发生的事情”之间的区别。在整个语言规范中都很常见。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多