ChatGPT解决这个技术问题 Extra ChatGPT

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

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);
}
Not a duplicate of stackoverflow.com/questions/16789832/…, thanks to the second half of the 2nd question.

C
Community

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.


B
Boann

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.)


W
Willi Mentzel

Looking at the byte code, if what is being returned does not match the definition, you will receive a compile error.

Example:

for(;;) will show the bytecodes:

L0
    LINENUMBER 6 L0
    FRAME SAME
    GOTO L0

Note the lack of any return bytecode

This does not ever hit a return, and thus does not return the wrong type.

For comparison, a method like:

public String getBar() { 
    return bar; 
}

Will return the following bytecodes:

public java.lang.String getBar();
    Code:
      0:   aload_0
      1:   getfield        #2; //Field bar:Ljava/lang/String;
      4:   areturn

Note the "areturn" which means "return a reference"

Now if we do the following:

public String getBar() { 
    return 1; 
}

Will return the following bytecodes:

public String getBar();
  Code:
   0:   iconst_1
   1:   ireturn

Now we can see that the type in the definition does not match the return type of ireturn, which means return int.

So really what it comes down to is that if the method has a return path, that path must match the return type. But there are instances in the bytecode where no return path is generated at all, and thus no breaking of the rule.