Why is return needed even after System.exit(0);

Solution 1:

Because as far as the compiler is concerned, System.exit() is just another method call.

The fact that what it does is end the process can only be found out from the implementation (which is native code, not that it makes any difference).

If you have to put System.exit() in your code (usually it's best to avoid it, unless you want to return a code other than 0), it should really be in a method that returns void, main() for example. It's nicer that way.

As for the reachability, the explanation is the same: return is a keyword of the Java language, so the compiler or the parser the IDE uses can tell that it's theoretically impossible for code after the return statement to be executed. These rules are defined here.

Solution 2:

The Java compiler doesn't know anything about System.exit. It's just a method as far as it's concerned - so the end of the statement is reachable.

You say that L1 and L2 are "visibly not reachable" but that's only because you know what System.exit does. The language doesn't - whereas it does know what a return statement does, so it knows that L3 really isn't reachable.

I sometimes think it would be useful to be able to declare that a method isn't just void, but never terminates normally - it never just returns (although it may throw an exception). The compiler would then be able to use that information to make the end of any calling expression unreachable, preventing this sort of thing from being a problem. However, that's just my dreams around language design - Java doesn't have anything similar, and it would be a very bad idea for the compiler to "know" that particular JRE methods will never return normally, when that concept can't be expressed directly within the language.

Instead, the compiler is bound by the rules of section 14.21 of the JLS, including:

  • The first statement in a non-empty block that is not a switch block is reachable iff the block is reachable.
  • Every other statement S in a non-empty block that is not a switch block is reachable iff the statement preceding S can complete normally.

...

An expression statement can complete normally iff it is reachable.

(A method call is an expression statement.)

Then from section 8.4.7:

If a method is declared to have a return type, then a compile-time error occurs if the body of the method can complete normally (§14.1).

and in 14.1:

Unless otherwise specified, a statement completes normally if all expressions it evaluates and all substatements it executes complete normally.

So the call to System.exit() can complete normally as far as the compiler is concerned, which means the body of the foo method can complete normally, which leads to the error.

Solution 3:

Me: "Can anything be executed after a return statement?"
Java: "NO."

Me: "Can anything be executed after I call System.exit?"
Java: "That's a method, I don't know what it does - it's not a reserved keyword, it doesn't affect program flow as far as I know" (and it may not even work (I don't know if exit can throw exceptions (or future variants of it)))

Solution 4:

From the language standpoint, there are only 2 ways to escape current scope: return and throw. Method calls are never considered the same way, even if they consist of the only line of code:

void method() {
  throw new RuntimeException();
}

Even more. In theory, any method call can cause RuntimeException to be thrown. In this case, compiler should probably give you warnings for absolutely any method call which is not inside a try block:

...
method(); // WARNING: may throw in theory
anotherMethod(); // WARNING: possible unreachable code, also may throw
anotherMethod2(); // WARNING: possible unreachable code, also may throw
// etc
...

For your question logic is the same.

Solution 5:

If a method is declared to return a non-void value, then it must contain a return statement somewhere, even if it's never reached (like in the code in the question).

From the compiler's point of view, System.exit() is just another method call, with nothing special about it that indicates that the program ends as soon as it's reached. Only you, as the programmer, know this fact - but it's something outside of the compiler's knowledge.

About the second part of your question - nothing can go after a return statement in a block of code inside a method, as that will always be unreacheable code. That explains why the compiler complains about the L3 line.