Java method with return type compiles without return statement
Question 1:
Why does the following code compile without having a return statement?
public int a() { while(true); }
This is covered by JLS§8.4.7:
If a method is declared to have a return type (§8.4.5), then a compile-time error occurs if the body of the method can complete normally (§14.1).
In other words, a method with a return type must return only by using a return statement that provides a value return; the method is not allowed to "drop off the end of its body". See §14.17 for the precise rules about return statements in a method body.
It is possible for a method to have a return type and yet contain no return statements. Here is one example:
class DizzyDean { int pitch() { throw new RuntimeException("90 mph?!"); } }
Since the compiler knows that the loop will never terminate (true
is always true, of course), it knows the function cannot "return normally" (drop off the end of its body), and thus it's okay that there's no return
.
Question 2:
On the other hand, why does the following code compile,
public int a() { while(0 == 0); }
even though the following does not.
public int a(int b) { while(b == b); }
In the 0 == 0
case, the compiler knows that the loop will never terminate (that 0 == 0
will always be true). But it doesn't know that for b == b
.
Why not?
The compiler understands constant expressions (§15.28). Quoting §15.2 - Forms of Expressions (because oddly this sentence isn't in §15.28):
Some expressions have a value that can be determined at compile time. These are constant expressions (§15.28).
In your b == b
example, because there is a variable involved, it isn't a constant expression and isn't specified to be determined at compilation time. We can see that it's always going to be true in this case (although if b
were a double
, as QBrute pointed out, we could easily be fooled by Double.NaN
, which is not ==
itself), but the JLS only specifies that constant expressions are determined at compile time, it doesn't allow the compiler to try to evaluate non-constant expressions. bayou.io raised a good point for why not: If you start going down the road of trying to determine expressions involving variables at compilation time, where do you stop? b == b
is obvious (er, for non-NaN
values), but what about a + b == b + a
? Or (a + b) * 2 == a * 2 + b * 2
? Drawing the line at constants makes sense.
So since it doesn't "determine" the expression, the compiler doesn't know that the loop will never terminate, so it thinks the method can return normally — which it's not allowed to do, because it's required to use return
. So it complains about the lack of a return
.
It can be interesting to think of a method return type not as a promise to return a value of the specified type, but as a promise not to return a value that is not of the specified type. Thus, if you never return anything, you are not breaking the promise, and so any of the following are legal:
Looping forever:
X foo() { for (;;); }
Recursing forever:
X foo() { return foo(); }
Throwing out an exception:
X foo() { throw new Error(); }
(I find the recursion one fun to think about: The compiler believes that the method will return a value of type X
(whatever that is), but it isn't true, because there is no code present that has any idea how to create or procure an X
.)