Java method with return type compiles without return statement

Willi Mentzel picture Willi Mentzel · Jun 25, 2015 · Viewed 16.5k times · Source

Question 1:

Why does the following code compile without having a return statement?

public int a() {
    while(true);
}

Notice: If I add return after the while then I get an Unreachable Code Error.

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);
}

Answer

T.J. Crowder picture T.J. Crowder · Jun 25, 2015

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.