Intellij空检查:无法访问的语句

时间:2018-07-11 09:25:00

标签: java intellij-idea null

此问题与this one类似,但重点有所不同

我有一个功能,可以在try catch块中将文件加载到磁盘上。由于该文件对于程序至关重要,因此如果无法加载该文件,它将终止:

String loadSuperImportantFile() {
    try {
        // ... file loading code ...
        assert fileContent != null;
        return fileContent;
    } catch(IOException ex) {
        System.err.println("Failed to load super important file. Please check path.");
        System.exit(Codes.FAIL); // terminate the program
    }
    return null; // statement is unreachable
}

IntelliJ IDEA无法识别此函数不可能返回null。因此,每当我使用返回字符串时,我都会收到警告:

foo(loadSuperImportantFile()); // WARNING: Argument "loadSuperImportantFile()" might be null.

通过阅读我上面链接的问题,我相信可以使用方法合同来告诉IntelliJ,该方法不能返回null。我尝试使用@Contract("null -> fail")装饰器,但警告并没有消失。

有人使用方法合同或类似方法,而不是从外部进行空检查,对如何使警告在方法本身中消失没有任何想法吗?

5 个答案:

答案 0 :(得分:4)

System.exit(Codes.FAIL);不是Java中的终止语句(例如returnthrow)。

在某些有线情况下,您可以想象,可以覆盖exit方法(或模拟它),而不会终止应用程序。然后将返回此null

对于简洁且健壮的应用程序,如果要终止该应用程序,则引发将传播的异常。特别是,如果要因为终止应用程序,则会发生错误(无效路径)。让应用程序自行死亡将更加实用。为什么必须致电System.exit();

PS:您还可以看到@Stephen C或@ user31601的答案,这肯定会解决方法返回null的问题(因为它使用流控制语句-throw),但是我不建议那种选择。我认为,最好设计一个更好的结构,即简洁的结构,不要让这种情况发生-相反,允许它发生,然后抛出{{1} }。

PS2:您也可以像建议@yole一样添加AssertionException,但是同样-代替反应来处理不应该发生的事情-只是不要不要让它发生。并抛出(例如)@NotNull

我的建议:

throw new InvalidPathException();

答案 1 :(得分:1)

  

IntelliJ IDEA无法识别此函数无法返回null

IntelliJ仅遵循标准Java可达性规则。他们说return null;语句是可到达的。

  

有人对如何使警告在方法本身中消失没有任何想法。

您可以将以下语句替换为:

   throw new AssertionError("unreachable statement executed");

或者更好的是,将其放在System.exit(...)调用之后。

任何不受检查的异常都可以,但是在我看来AssertionError是发生完全错误的最有力的指示。请注意,您需要显式引发异常。使用assert不足以避免需要return ...,因为可以关闭断言检查。

从未执行的throw语句的运行时开销最小为零。

另一个想法是返回一个虚拟的非null值。在此示例中,将使用空字符串。


将方法注释为@NotNull的问题是静态代码分析器(使用与IntelliJ相同的不完全逻辑)可能会标记方法确实返回null。如果您可以抑制该警告,则还有可能某些框架可能会插入null的运行时检查... 1)没有用,2)可能无法优化。


不幸的是,没有一种实用的方法可以使用注释“此语句永不返回”的注释来标记System.exit。为什么?因为它是一种标准方法,所以您不能更改它,而...除非对类库进行黑客攻击并(可能)破坏其可移植性。

(我认为)解决该问题的最佳办法是开发补丁并将其提交给Intellij维护人员。 “了解” System.exit特殊行为的人。


最后,对于库方法来说,以您示例中的方法调用System.exit通常是个坏主意。更好的方法是在应用程序的控制线程上引发一个自定义异常,该异常会在调用堆栈的底部/附近捕获。这样,您可以将所有用于控制(受控)应用程序出口的代码和逻辑都放在同一位置。

辅助方法不应决定整个应用程序的“命运”。

答案 2 :(得分:0)

为什么不只是在catch块的末尾添加终止语句?它永远不会实现,因此它所要做的就是帮助编译器推断出最终的return语句是不必要的。

例如:

String loadSuperImportantFile() {
    try {
        // ... file loading code ...
        assert fileContent != null;
        return fileContent;
    } catch(IOException ex) {
        System.err.println("Failed to load super important file. Please check path.");
        System.exit(Codes.FAIL); // terminate the program
        throw new AssertionError("Unreachable");
    }
}

答案 3 :(得分:0)

只需将方法注释为@NotNull。您尝试应用的@Contract注释表示,如果您将null作为参数传递给该方法,则该方法将失败,这是没有意义的,因为它没有任何参数。

答案 4 :(得分:0)

您还可以返回Optional<String>而不是String

这样,您可以避免返回null并返回Optional.empty(),以避免空检查和可能的NullPointerException

请参阅:https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html