【问题标题】:Is there a "noreturn" keyword in JavaJava中是否有“noreturn”关键字
【发布时间】:2011-08-01 02:27:05
【问题描述】:

有时我想写一个 error() 函数,它最终肯定会调用 System.exit(),这意味着这个函数永远不会返回。但是,如果我在其他函数中调用 error(),我想这样写:

int fun() {
...
  error();
}

但是编译器坚持在 error() 调用之后返回一个 int 值,因为它不知道 error() 永远不会返回。 我当然可以返回一个任意的 int,但是如果返回类型是一个复杂的类,我需要在代码中构造它,这很浪费时间。有没有办法告诉编译器一个函数永远不会返回?

【问题讨论】:

  • 您的问题的答案是“否”。对 Java 语言手册的快速检查表明了这一点。为什么这么问?更重要的是,为什么不抛出异常而不是调用System.exit()

标签: java compiler-construction return


【解决方案1】:

在 Java 方法签名中,您无法告诉编译器该方法无法返回。没有它,编译器别无选择,只能假设它可能会返回。 (事实上​​,关于可达性/明确分配的 JLS 规则将明确说明这一点……如果您愿意涉足它们。)

我在两种方法之间摇摆不定:

int fun() {
    ...
    error();
    return 0;  // NOT REACHED
}

int fun() {
    ...
    error();
    throw new AssertionError("not reached");
}

两者都不完全令人满意。

在第一个版本中,注释很重要,因为第一次阅读您的代码的人可能不会意识到error 永远不会返回。

第二个版本更健壮,因为如果有人更改 error 方法的行为以使其实际返回,它将给您一个“快速失败”。 (第一个版本会导致fun 方法返回一个虚假值……可能会导致其他意想不到的后果。)


在相关点上,通过调用System.exit() 来拔出 JVM 插头的方法可能会出现问题。一个更好的主意是抛出一个未经检查的“世界末日”异常并在主线程的最外层捕获它。对于在其他线程上引发的异常,一种想法是安装一个默认的未捕获异常处理程序,该处理程序使用Thread.interrupt() 通知主线程已引发“世界末日”异常。

但关键是在代码深处进行的System.exit() 调用是有问题的;例如如果您重新调整代码的用途以在更大的框架内运行;例如在网络容器中。

【讨论】:

  • 我不明白为什么java没有类似于void的noreturn方法类型...
  • 这是一个设计决定。他们要么通过提供特殊的语法来支持这种极端情况而使语言复杂化,而且人们抱怨额外的复杂性。或者他们没有,并且(不同的)人们抱怨边缘情况没有得到很好的支持。他们必须做出决定。他们无法取悦所有人。
【解决方案2】:

你应该抛出一个RuntimeException

【讨论】:

  • 甚至子类化它来创建一个 NotPossibleException,这样如果它被抛出,你就知道肯定有严重错误,并且代码更清晰。
  • @mathepic,如果 OP 抛出 RuntimeException 异常而不是调用 System.exit()(应该是这种情况),那么实际上它肯定是可能的抛出异常。
  • 没关系,我想我误读了你的建议。我以为你的意思是错误然后 RuntimeException 阻止编译器抱怨/
【解决方案3】:

Java 不允许你声明你的方法永远不会返回,但是编译器知道一个永远不会返回的语句throw

您可以在您的情况下通过声明“不返回”方法来利用它来返回RuntimeException,并使用throw error() 调用它。这样你就可以将“不返回的方法”的问题转移到“不返回的语句”上。

由于您的error() 方法永远不会返回,因此您不需要实际返回声明的RuntimeException,并且throw error() 中的throw 永远不会执行。

error() 确实返回时,这种技术还可以防止您出现错误,这可能是因为一些错误的条件代码。

例子:

private RuntimeException error() {
    throw new EndOfTheWorldException();
    // Java knows that code after throw will not be reached
    // so no return is required here.  Or, if you insist, you
    // could call System.exit() and return null afterwards.
}

int fun() {
    // ...
    throw error();
    // Again, no need for a return here, the compiler understands
    // that the statement above won't return.
}

或者,也许更明确地,您可以重构此代码以使 error() 成为引发异常的工厂:

private RuntimeException makeError() {
    return new EndOfTheWorldException();
}

int fun() {
    throw makeError();
}

【讨论】:

    【解决方案4】:

    返回 0,或者在复杂情况下,返回 null。

    【讨论】:

    • 如果返回类型是int,则不能返回null,因为它们是不兼容的类型。
    • 是的,然后返回0,或者在复杂的情况下,返回null;
    • -1 表示'return null',因为具体问题是关于返回类型 int (以及您在自己的答案中添加的评论)。
    • @Ken,如果你仔细阅读这个问题,你会注意到:“我肯定可以返回一个任意的int,但是如果返回类型是一个复杂的类,我需要在代码中构造它这是浪费时间”。至于“后来添加”来回答,Gnostus 的回答是他写的,所以没有编辑过……
    • 撤回反对票。我误读了这个问题。至于我对评论的评论,这是我所指的这个线程中的第二个comment(大约比这个高四个); Gnostus 只是简单地重复了最初的回答,前面是“是的,那么”。我提出这一点的评论可能也应该被删除,因为它是不恰当的;不过,把它留给后面的其他 cmets 做上下文,包括我承认我的错误。
    【解决方案5】:

    不确定您为什么要这样做。如果您抛出异常,它将是更简洁的代码,更易于测试和维护;你可以使用Thread.setDefaultUncaughtExceptionHandler() 退出。

    但如果你坚持:

    public class HaltAndCatchFireError extends Error {
       public HaltAndCatchFireError() {
          System.exit(1);
       }
    }
    

    然后你想退出的地方:

    int fun() {
       ...
       throw new HaltAndCatchFireError();
    }
    

    【讨论】:

    • 我认为这是个坏主意。 1)看起来你正在抛出异常。 2)如果你真的执行了那个构造函数,就会发生一些非常糟糕的事情。
    • @Stephen 哦,当然。但是在您的代码周围随机散布 System.exit() 已经是个坏主意。 :)
    【解决方案6】:

    你的函数有没有返回任何东西?如果不是,您希望您的功能无效,似乎:

    void fun() {
    
    ...
    
    
    }
    

    【讨论】:

      【解决方案7】:

      你可以写

      error();
      return;
      

      但您可以重新考虑您的方法。这可能不是一个非常面向对象的。

      【讨论】:

      • 它真的和面向对象没有任何关系。这是一个语法/语义问题。 Java 是否合法,当你应该返回一个 int 时不返回任何东西,即使你从来没有真正“返回”?一般不会,这就是问题的原因。
      • 您试图破坏 api 的合同(即:返回 int 或抛出异常)。更好的方法是 throw RuntimeException
      • 这是一个糟糕的答案加上抛出随机运行时异常是不好的,这无论如何都是语义上的死代码
      猜你喜欢
      • 2011-03-13
      • 2016-03-11
      • 1970-01-01
      • 2016-08-02
      • 2011-08-09
      • 1970-01-01
      • 2010-12-31
      相关资源
      最近更新 更多