为啥方法在抛出异常后不需要返回值?

Posted

技术标签:

【中文标题】为啥方法在抛出异常后不需要返回值?【英文标题】:Why don't methods need to return a value after throwing an exception?为什么方法在抛出异常后不需要返回值? 【发布时间】:2022-01-14 22:38:41 【问题描述】:

这是一个人为的例子。我有一个非 void 方法,它会引发异常。为什么我不必事后返回一个值?毕竟,方法是非无效的。

public static Toast makeText(Context context, CharSequence text, int duration) 
    throw new RuntimeException("Stub!");
    //Must return something from here but there is not, Why?

【问题讨论】:

该方法总是无条件地以异常结束。以异常结束的方法没有返回值,因为它抛出了异常。我建议您学习 Java 中异常的基础知识以及如何处理它们。 你在哪里使用这个方法! 嗨。我建议你谷歌“函数中的 return 语句如何工作”。当你有基本的想法时,你应该谷歌“抛出和处理异常”。互联网上有大量信息。祝你好运! 异常进入一个特殊的控制流并结束常规的方法控制流。该方法是 exit,Java 会遍历调用堆栈,直到找到捕获此异常事件的 try-catch。如果没有找到,程序结束。特别是,该方法不继续,因为它没有“正常”结束,所以它也没有返回任何值。 @Zabuzard 首先感谢您编辑标题,因为我的标题不清楚。正如你所说,我将研究 Java 中的异常,因为我不记得我做过这样的事情。谢谢。 【参考方案1】:

抛出异常会中断控制流,立即退出方法。当抛出异常时,不需要返回值,因为调用该方法的代码没有正常完成。例如,在下面的代码中,foo 不需要返回数字,因为int x = foo(); 不成功,而是传播异常:

int foo() 
    throw new RuntimeException();

void bar() 
    int x = foo();
    // This line will not be reached
    System.out.println(x);

由于int x = foo();之后的代码无论如何都不会执行,所以x不需要从foo接收返回值,因此foo不需要提供返回值。

事实上,一个方法不能既返回值又抛出异常,因为返回值意味着方法正常完成。

【讨论】:

补充一点,假设bar() 的作者不知道foo() 可以抛出并想象bar() 将是非无效的。如果方法在得到异常后需要返回值,他怎么知道int x = foo(); return x + 5; 是不安全的并且没有到达最后一行?如果你仔细考虑一下,那将是完全混乱的。 @Zabuzard 如果假设方法即使在抛出异常时也需要返回值,那么int x = foo(); return x + 5; 就可以了; x 会有一个值,因为 foo 必须返回一个值。这只是意味着异常必须不中断控制流。你可以设计这样的语言;实际上它与在 C 中处理错误的方式非常相似,在 C 中不是抛出异常来指示错误条件,而是设置错误标志并继续执行,假设调用者在关心是否发生错误时会检查错误标志. “因为返回值意味着方法正常完成”这是不正确的,或者至少使用了与 Java 中“正常完成”的含义不同的术语:返回值被认为是异常完成。 @AndyTurner 表示方法invocation会正常完成,即int x = foo();这一行。根据 JLS,return 语句本身不会“正常完成”,但 return 语句不是方法。 @kaya3 然后说方法调用:)【参考方案2】:

实际上并没有要求返回值的方法包含返回语句。也许令人惊讶的是,这段代码是合法的:

int noReturn() 
  while (true) 

语言规范的关键是JLS 8.4.7,它说:

如果将方法声明为具有返回类型(第 8.4.5 节),则如果方法的主体可以正常完成(第 14.1 节),则会发生编译时错误。

JLS 14.1中描述了“正常完成”:

每个语句都有一个正常的执行模式,其中执行某些计算步骤。以下部分描述了每种语句的正常执行模式。

如果所有步骤都按描述执行,没有任何突然完成的迹象,则称该语句正常完成。但是,某些事件可能会阻止语句正常完成:

break、yield、continue 和 return 语句(第 14.15 节、第 14.21 节、第 14.16 节、第 14.17 节)会导致控制权转移,这可能会阻止包含它们的表达式、语句和块的正常完成。 某些表达式的计算可能会从 Java 虚拟机中引发异常(第 15.6 节)。显式 throw (§14.18) 语句也会导致异常。异常会导致控制权转移,这可能会阻止语句的正常完成。

所以:要求是方法不能正常完成;而returnthrow都是导致方法异常完成的方式。


请注意,这并不是说该方法必须异常完成:返回到开头的 while 循环示例,它不会正常或异常完成:因为循环条件是一个常量 true,并且不会包含 return、throw 或 break 或可能引发异常的语句,该循环永远不会完成,这也很好(至少从语言的角度来看)。

【讨论】:

以上是关于为啥方法在抛出异常后不需要返回值?的主要内容,如果未能解决你的问题,请参考以下文章

了解Java在抛出异常时的堆栈展开

为啥我应该在抛出异常指针时使用按引用捕获

为啥内部异常到达 ThreadException 处理程序而不是实际抛出的异常?

谁在抛出(并捕获)这个 MySQL 异常?

Java中子类重写父类的方法为啥返回值类型要小于等于父类方法的返回值类型?

SmtpClient.SendMailAsync 在抛出特定异常时导致死锁