65

I am wondering why is the following code allowed in Java, without getting compilation error? In my opinion, this code breaks method signature by not returning any String. Could someone explain what I'm missing here?

public class Loop {

  private String withoutReturnStatement() {
    while(true) {}
  }

  public static void main(String[] a) {
    new Loop().withoutReturnStatement();
  }
}
xaizek
  • 5,098
  • 1
  • 34
  • 60
Simo Martomaa
  • 526
  • 4
  • 9
  • 15
    Because the compiler is smarter than you think, and see that the function will not return? – Some programmer dude Aug 06 '14 at 07:26
  • 1
    It's not only interesting question but also a `hot network question` :D – Suresh Atta Aug 06 '14 at 08:31
  • 3
    @sᴜʀᴇsʜᴀᴛᴛᴀ: and answered by Jon Skeet... – PlasmaHH Aug 06 '14 at 09:14
  • great question… and to my surprise this is also true for C# – Marcel B Aug 06 '14 at 11:32
  • @MarcelB Not sure why that should be surprising: other than a handful of cases where the C# designers have apparently learned from the mistakes the Java designers made, C# and Java are very similar languages. I don't see this as a mistake, so it is unsurprising they should behave similarly. – Jules Aug 07 '14 at 06:14
  • 12
    While @JonSkeet's answer is great, I think it doesn't emphasize the basic point: the return type in a method signature isn't a promise that the method will eventually return an object of that type (such a promise would be impossible for the compiler to verify due to the non-computability of the halting problem) but rather it is a promise that *if the method returns, the returned value will be of that type*. – Jules Aug 07 '14 at 06:16
  • @Jules: sorry bad phrasing… What I really want to say is, that this fact is surprising for me (but not as much after I thought about it for a while) and it’s also true for C#. – Marcel B Aug 07 '14 at 06:46

2 Answers2

82

The final } of the method is unreachable - you only get a compilation error if it's possible to get to the end of the method without returning a value.

This is more useful for cases where the end of the method is unreachable due to an exception, e.g.

private String find(int minLength) {
    for (String string : strings) {
        if (string.length() >= minLength) {
            return string;
        }
    }
    throw new SomeExceptionIndicatingTheProblem("...");
}

The rule for this is in the JLS section 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).

Your method can't complete normally, hence there's no error. Importantly, it's not just that it can't complete normally, but the specification recognizes that it can't complete normally. From JLS 14.21:

A while statement can complete normally iff at least one of the following is true:

  • The while statement is reachable and the condition expression is not a constant expression (§15.28) with value true.
  • There is a reachable break statement that exits the while statement.

In your case, the condition expression is a constant with value true, and there aren't any break statements (reachable or otherwise) so the while statement can't complete normally.

Community
  • 1
  • 1
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Throwing the exception makes sense. The surprising part is that `while(true) {}` "counts". Especially coming from a language that won't even compile is it knows there is unreachable code. – Paul Draper Aug 06 '14 at 15:18
  • @PaulDraper: I'm not sure what you mean. In both cases (infinite loop and exception) you can't reach the end of the method normally, so you don't need to return. Put it this way - if you *did* have a `return` statement at the end, that would be unreachable... not good. – Jon Skeet Aug 06 '14 at 15:19
  • 1
    I should be more clear. The answer is 100% correct, and `while(1) {}` fits the JLS you quoted. I am just surprised that they chose to design it that way, and implementers chose to inspect the conditions of `while` loops. It's a Halting Problem, a losing battle. `while(1) {}` compiles, but `while("foo".length() > 0) {}` doesn't compile, at least with OpenJDK 7. Now, another Java compiler could choose to be more sophisticated and recognize that case. Suddenly your write-once-run-everywhere code is broken. – Paul Draper Aug 06 '14 at 15:30
  • 4
    @PaulDraper: No, a compiler could *not* choose to be more sophisticated. The language specification is pretty clear about reachability. "A while statement can complete normally iff at least one of the following is true: The while statement is reachable and the condition expression is not a constant expression (§15.28) with value true. [...]". `"foo".length() > 0` is not a constant expression. Java isn't trying to solve the halting problem - it's just defining some clear rules which compilers must obey which address a *very few* situations where the result is known for certain at compile-time. – Jon Skeet Aug 06 '14 at 15:33
  • @PaulDraper: The spec isn't about just simplicity. If there exists a legitimate Java compiler that would require a `return` or `throw` at the end of a particular function, then even if there's no actual execution path which would reach that statement, it would be very bad for any other compiler to squawk that such a statement was "unreachable". – supercat Aug 06 '14 at 15:57
  • IMO the comment about "A while statement can complete normally iff ..." could do with a reference and be added to the body of this answer, to make it even more super awesome. – Michael Anderson Aug 07 '14 at 02:56
  • The code as is, after being threaded in the parser, will not reach the end bracket, so it is unreachable. Now what if it is: ``bool foo = true; while (foo) {}``? In non-optimized threaded code, the end bracket is reachable. However, after optimization which propagates constants, the end bracket becomes unreachable. According to the JLS, ``while`` doesn't complete normally if the condition is a "constant expression" evaluating to true. However, in this case, ``foo`` is a variable which always holds true. So according to the JLS, this is a compile-time error? What if after optimizations? – Stephen Chung Aug 13 '14 at 02:43
  • 1
    @StephenChung: Note that the term "constant expression" is well defined by the JLS ([§15.28](http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.28)), so each conforming compiler is required to behave the same. For example `bool foo=true; while(foo){}` would require a return statement as `foo` does not refer to a constant expression as specified by the JLS. However `final bool foo=true; while(foo){}` does not allow a return as `foo` is a constant expression in this case. – siegi Aug 15 '14 at 18:32
22
 private String withoutReturnStatement() {
    while(true) {
        // you will never come out from this loop
     } // so there will be no return value needed
    // never reach here ===> compiler not expecting a return value
  }  

To more clarification try this

private String withoutReturnStatement() {
    while(true) {}
    return ""; // unreachable
}

It says unreachable statement

Ruchira Gayan Ranaweera
  • 34,993
  • 17
  • 75
  • 115