Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Java compiler refuse to recognize System.exit() as a procedure termination?

Java's compiler, at least the one from Oracle that I use, refuses to recognize System.exit() as a procedure termination. For example, the following code gives a compilation error:

public static int readInteger( ArrayList<String> listLines, int iLineNumber0 ){
    try {
        int value = Integer.parseInt( listLines.get( 0 ) );
        return value;
    } catch( Throwable t ) {
        System.err.println( "error reading line: " + iLineNumber0 + ": " + t );
        System.exit( -1 );
    }
}

The error is: "Missing return statement." Therefore, to make this work I have to add in a return statement like this (compiles successfully):

public static int readInteger( ArrayList<String> listLines, int iLineNumber0 ){
    try {
        int value = Integer.parseInt( listLines.get( 0 ) );
        return value;
    } catch( Throwable t ) {
        System.err.println( "error reading line: " + iLineNumber0 + ": " + t );
        System.exit( -1 );
    }
    return 0; // unreachable code
}

Ironically, the final return statement that is needed is unreachable code, although the compiler does not realize this either.

like image 509
Tyler Durden Avatar asked Dec 14 '22 23:12

Tyler Durden


2 Answers

Java's compiler, at least the one from Oracle that I use, refuses to recognize System.exit() as a procedure termination.

Yes, it would. As far as the compiler is concerned, it's just a void method. There's no way of indicating "this method never returns normally" in a method signature, and no such concept in the language. For example:

public void alwaysThrow()
{
    throw new RuntimeException();
}

...

alwaysThrow();
System.out.println("This line is never reached");

The last line in the above snippet is still reachable as far as the compiler is concerned, even though we know it will never execute. Likewise your extra return statement is technically reachable, but practically unreachable.

Basically this could be deemed a flaw in the language, although it's one which affects most languages as far as I'm aware. While it would be nice to be able to represent methods like this, it's rarely a real issue in real life.

If you find yourself bothered by it, you could write a helper method:

public RuntimeException systemExit(int exitValue)
{
    System.exit(exitValue);
    return new RuntimeException("Shouldn't get here");
}

Then call it as:

throw systemExit();

That will ensure that the end of the statement is unreachable as far as the compiler is concerned, so you could have:

catch (Throwable t) {
    System.err.println("error reading line: " + iLineNumber0 + ": " + t);
    throw systemExit(-1);
}

... and your compiler error would go away.

Note that there are other similar situations where reachability isn't everything we might want. For example:

int foo() {
    int x = someValue();
    if (x > 10) {
       return 1;
    }
    if (x <= 10) {
       return 20;
    }
    // Is this reachable or not?
}

We know that any value of x will either be greater than 10 or less-than-or-equal-to 10, so the final line is practically unreachable but the rules of the language don't express that... so even a smart compiler can't actually treat the above code as valid without violating the language specification.

like image 163
Jon Skeet Avatar answered May 10 '23 12:05

Jon Skeet


Consider this: you've loaded a different runtime library whose System.exit does not exit. Now what will happen at the end of the method? The compiler would rather have a concrete answer about this, so it doesn't make any special assumptions about System.exit (or Runtime.exit).

If it did, this would only add complications to the language specification for no real benefit. Exit calls are not common, and indeed, your code is not a good example of using one, because even if it did not require the unreached return statement, it still hides the original exception that caused the problem. I would replace the println/exit calls with:

throw new RuntimeException("Error reading line " + iLineNumber0, t);

When an exit call is really needed in a non-void method, it is easy to add a throw null; or throw new Error(); or return ...; below it.

Ironically, the final return statement that is needed is unreachable code, although the compiler does not realize this either.

This feature can't be added as it would break existing code.

like image 39
Boann Avatar answered May 10 '23 12:05

Boann