Java中是否有“noreturn”关键字?

4
有时候我想写一个error()函数,在最后一定会调用System.exit(),这意味着这个函数永远不会返回。但是,如果我在其他函数中调用error(),我希望像这样写:
int fun() {
...
  error();
}

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


4
你的问题的答案是“不”。Java语言手册的快速检查显示了这一点。为什么要问这个问题?更重要的是,为什么不抛出异常而是调用System.exit()呢? - S.Lott
7个回答

6

在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()调用可能会有问题;例如,如果您重新设计您的代码以在更大的框架内运行;例如,在Web容器中。


我不明白为什么Java没有类似于void的noreturn方法类型... - Enerccio
这是一个设计决策。 它们要么通过提供特殊语法来支持此边缘情况而使语言复杂化,人们会抱怨额外的复杂性。 要么它们不提供,(不同的)人们抱怨该边缘情况得不到很好的支持。 他们不得不做出决定。 他们不能取悦所有人。 - Stephen C

5

甚至可以子类化它以创建一个NotPossibleException,这样如果它被抛出,你就知道肯定有严重的问题,并且代码更清晰。 - alternative
@mathepic,如果OP抛出RuntimeException异常而不是调用System.exit()(应该是这样的情况),那么实际上肯定会有可能抛出异常。 - Kirk Woll
没事,我误解了你的建议。我以为你是指使用错误和RuntimeException来阻止编译器抱怨。 - alternative

4
Java不允许您声明您的方法永远不会返回,但编译器知道一个永远不会返回的语句:throw。您可以通过声明您的“无返回值”方法返回RuntimeException,并使用throw error()调用它来利用它。这样,您就将“不返回的方法”的问题转移到了“不返回的语句”。由于您的error()方法永远不会返回,因此您不需要实际返回声明的RuntimeException,并且在throw error()中的throw永远不会执行。这种技术还可以防止由于一些有缺陷的条件代码而导致的错误。例如:
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();
}

1

不确定为什么你想这样做。如果你抛出一个异常,代码会更加清晰、易于测试和维护;你可以使用 Thread.setDefaultUncaughtExceptionHandler() 来退出。

但是如果你坚持:

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

然后在你想要退出的地方:

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

2
我会称之为一个糟糕的想法。1)看起来你正在抛出一个异常。2)如果你真的执行了那个构造函数,会发生非常糟糕的事情。 - Stephen C
@Stephen Oh,当然。但是在您的代码中随机插入System.exit()已经是一个坏主意了。 :) - David Moles

1

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


1
如果返回类型是int,你不能返回null,因为它们是不兼容的类型。 - edwardsmatt
是的,然后返回0,或在复杂情况下返回null; - Jake Kalstad
由于具体问题是关于返回类型int的,因此对于'return null',应该返回-1(并针对您自己的答案后添加的注释)。 - Ken White
1
@Ken,如果你认真阅读问题,你就会注意到:“我肯定可以返回任意int,但如果返回类型是复杂类,则需要在代码中构造它,这是浪费时间”。至于“事后添加”到答案中,Gnostus的答案自他写下以来并没有被编辑... - meriton
撤销反对票。我误读了问题。至于我对评论所做的评论,我指的是这个线程中的第二个评论(在此之上约四个)。Gnostus只是重复了最初的答案,并在前面加上“是的,然后”。我发表那个观点的评论可能也应该被删除,因为它是不合适的;但是为了让其他评论有上下文,包括我承认错误的评论,应该保留。 - Ken White

0

你的函数是否有返回值?如果没有,你想让你的函数成为 void 类型:

void fun() {

...


}

-2

你可以写

error();
return;

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


这实际上与面向对象无关。这是一个语法/语义问题。如果你应该返回一个int,却什么也不返回,即使你根本没有“返回”,那么这是否是合法的Java呢?通常而言并不是,这也是提出问题的原因。 - cHao
你试图违反API的契约(即:返回int或抛出异常)。更好的方法是抛出RuntimeException - bluefoot
那是一个可怕的答案,再加上抛出随机运行时异常是不好的,而且这段代码在语义上已经死了。 - Enerccio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接