Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java method with return type compiles without return statement

People also ask

Can a function be compiled without a return statement?

In other words, a return call can also be used to terminate procedures and anonymous code blocks without returning a value (as a function would) to the caller. So this is why a function will compile without an error when missing a return.

Can a method have no return statements Java?

Any method that is not declared void must contain a return statement with a corresponding return value, like this: return returnValue; The data type of the return value must match the method's declared return type; you can't return an integer value from a method declared to return a boolean.

What happens if a method does not have a return statement?

If no return statement appears in a function definition, control automatically returns to the calling function after the last statement of the called function is executed. In this case, the return value of the called function is undefined.

What is the method without return type in Java?

The void Keyword This method is a void method, which does not return any value.


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:

  1. Looping forever:

    X foo() {
        for (;;);
    }
    
  2. Recursing forever:

    X foo() {
        return foo();
    }
    
  3. 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.)


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.