为什么Java编译器拒绝将System.exit()识别为过程终止?

时间:2014-05-03 15:38:55

标签: java compiler-errors

Java编译器,至少是我使用的Oracle编译器,拒绝将System.exit()识别为过程终止。例如,以下代码给出了编译错误:

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

错误是:&#34;缺少退货声明。&#34;因此,为了完成这项工作,我必须在这样的返回语句中添加(编译成功):

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
}

具有讽刺意味的是,所需的最终返回语句是无法访问的代码,尽管编译器也没有意识到这一点。

4 个答案:

答案 0 :(得分:10)

  

Java编译器,至少是我使用的Oracle编译器,拒绝将System.exit()识别为过程终止。

是的,它会的。就编译器而言,它只是一种void方法。没有办法表明&#34;这种方法永远不会正常返回&#34;在方法签名中,语言中没有这样的概念。例如:

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

...

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

就编译器而言,上述代码段中的最后一行仍然可以访问,即使我们知道它永远不会执行。同样,您的额外return语句技术上可以访问,但几乎无法访问。

基本上这可能被认为是语言中的一个缺陷,尽管它是影响大多数语言的一个例子,据我所知。虽然能够代表这样的方法会很好,但它很少很少成为现实生活中的真正问题。

如果你发现自己被它困扰,你可以写一个辅助方法:

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

然后将其称为:

throw systemExit();

这将确保在编译器方面无法访问语句的结尾,因此您可以:

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

...并且你的编译器错误会消失。

请注意,还有其他类似情况,可达性不是我们可能想要的所有内容。例如:

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

我们知道x的任何值都将大于10或小于或等于10,所以最后一行几乎无法访问,但语言规则并未表达......因此,即使是智能编译器也无法在不违反语言规范的情况下将上述代码视为有效。

答案 1 :(得分:2)

请考虑这一点:您已加载了System.exit未退出的其他运行时库。现在该方法结束时会发生什么?编译器宁愿对此有一个具体的答案,因此它不会对System.exit(或Runtime.exit)做出任何特殊的假设。

如果确实如此,这只会增加语言规范的复杂性,从而没有真正的好处。退出调用并不常见,实际上,您的代码不是使用它的好例子,因为即使它不需要未到达的return语句,它仍然会隐藏导致问题的原始异常。我会用:

替换println / exit调用
throw new RuntimeException("Error reading line " + iLineNumber0, t);

如果在非空方法中确实需要退出调用,则可以在其下方添加throw null;throw new Error();return ...;

  

具有讽刺意味的是,所需的最终返回语句是无法访问的代码,尽管编译器也没有意识到这一点。

无法添加此功能,因为它会破坏现有代码。

答案 2 :(得分:0)

错误不是因为System.exit()代码,而是方法需要catch中的返回值,但syetm.exit返回类型为void。

完整的情景说明。

任何方法,如果它需要return值,那么方法应该在所有可能的情况下返回一个值,并且在所有情况下也只有一个return

但在你的第一个案例中

你只把return放在试试中但不放在捕获上,所以它正在抱怨预期的返回声明。因为当异常发生时没有返回并且它被捕获在该catch块下。

在你的第二个案例中,

您已将return置于try和try / catch之外。然后返回变为无法访问的代码,因为try已经返回,所以它不应该有多个返回,因为在尝试了一次在java中非法的返回之后。

所以解决方案

在try中输入并捕获两者但不在try / catch之外。

直接将注册回到try和catch。

解决方案1程序

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

解决方案2程序

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

让我知道任何疑问。

答案 3 :(得分:0)

您可以在try-catch块之外返回值:

public static int readInteger( ArrayList<String> listLines, int iLineNumber0 ){
    int value = 0;

    try {
        value = Integer.parseInt( listLines.get( 0 ) );
    } catch( Throwable t ) {
        System.err.println( "error reading line: " + iLineNumber0 + ": " + t );
        System.exit( -1 );
    }

    return value;
}