Java 在抛出 RuntimeException 时需要 return 值

Java expects return value when RuntimeException is thrown

为什么不能编译(用 java 8 和 java 10 试过)?它会产生缺少 return 语句的错误。

 public class CompilerIssue {
   public boolean run() {
     throwIAE();
     // Missing return statement
   }

   public void throwIAE() {
     throw new IllegalStateException("error");
   }
 }

您的方法具有 return 类型,因此它应该 return 布尔结果。

   public boolean run() {
     throwIAE();
     // Missing return statement
     return false;
   }

或者您应该直接在方法中抛出异常:

   public boolean run() {
     throw new IllegalStateException("error"); // it will compile
   }

java 编译器不知道 throwIAE 总是会抛出异常所以它 假设你最终会到达终点 run 方法和, 当这种情况发生时, return 值是必需的。

抛出的异常会终止 throwIAE 方法,但不会终止调用此方法的方法。将此理解为返回输出的方法(自 void 以来为空 return;)。

public void throwIAE() {
     return;                // is not responsible for terminating run()
}

看到了吗? return 终止 throwIAE 方法,程序继续。 returnthrow 都与方法本身的范围有关,与调用方法无关。您需要:

public boolean run() {
     throwIAE();            // throws exception and terminates throwIAR()
     return false;          // terminates run()
}

即使现在的编译器很聪明,他们也不能 'see' 你的 throwIAE() 方法总是 return 是一个异常,因为这会在运行时而不是编译时发生。如果你处理异常怎么办?那么你最终必须添加一个return值。

虽然其他答案从用户的角度正确地解释了这种情况,但值得注意的是,分析此类流的“聪明”不是由编译器决定的,而是由 JLS 的规则精确定义的。特别是,§14.21 包含以下内容:

An expression statement can complete normally iff it is reachable.

throwIAE()这样的方法调用是一个表达式语句,因为在例子中是可达的,所以也可以“正常完成”。因此,方法体可以“正常完成”,根据 §8.4.7 对于具有非 void return 类型的方法来说是非法的(也就是说,这样的方法必须 return 所有可能路径上的值)。

换句话说,JLS定义了在流分析中所有的方法调用都被平等对待,并没有“method-that-always-throws”这样的概念。

目前回答得很好。我不知道怎么没人提到 AOP(Aspect Oriented Programming)。使用 AOP,您可以在运行时轻松拦截您的方法 throwIAE() 并捕获异常。调用方法 run() 无法知道这一点。

一些例子:

package yourPackage;

public class YourClass {

    public boolean run() {
        throwIAE();
        System.out.println("Hello world!");// prints Hello World! even if method throws exception
        return false;// does not compile without return statement
    }

    public void throwIAE() {
        throw new IllegalStateException("error");
    }

}

另一个方面 class 在运行时拦截 throwIAE() 方法:

package yourPackage;

@Aspect
public class ExceptionHandlingAspect {
    @Around("execution(* yourPackage.YourClass.throwIAE())")
    public Object handleException(ProceedingJoinPoint pjp) throws Throwable {
        try {
            return pjp.proceed();
        } catch (Exception e) {
            System.out.println("exception caught");
            return null;
        }
    }
}  

想象一下:

 public class NotReallyACompilerIssue extends CompilerIssue {

   @Override    
   public void throwIAE() {
     // doesn't really throw the IAE!
   }
 }

如果您的原始 class 编译时没有 run() 中的 return 语句,NotReallyACompilerIssue 中的 run() 方法应该成功完成并且没有return。哪个不好,不是吗?

如果class是final,或者方法throwIAE是final,就可以检测到这种情况。但事实并非如此,编译器(至少是 Oracle J8_131)不会进行此类检查。我想这是一个非常具体的案例,不值得付出努力。

我真的没想到这会起作用,但有趣的是你可以这样做,这使代码更具可读性:

    public static <T> T unsupported()
    {
        throw new UnsupportedOperationException();
    }

     ...
    
    return unsupported();