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



这个问题与这个问题相似,但重点有所不同

我有一个函数,可以在磁盘上的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")装饰器,但警告并没有消失。

有人知道如何使用方法合同或类似的东西,而不是外部的null检查,使警告在方法本身中消失吗?

System.exit(Codes.FAIL);在Java中不是终止语句(与returnthrow类似)。

在一些有线的情况下,您可以想象,可以覆盖exit方法(或模拟它),这样它就不会终止应用程序。则该null将被返回。

对于简洁而健壮的应用程序,如果您想终止应用程序,请抛出一个会向上传播的Exception。特别是,如果您想终止您的应用程序,因为发生了错误(无效路径)。让应用程序自行消亡会更加务实。为什么你必须打电话给System.exit();

附言:你也可以看到@Stephen C或@user31601的答案,这肯定会解决你的方法返回null的问题(因为它使用流控制语句-throw),但我不建议使用这个选项。在我看来,最好设计一个更好的结构,一个简洁的结构,不要让这种情况发生——相反,允许它发生,然后在发生时抛出AssertionException

PS2:你也可以像@yole建议的那样添加@NotNull,但再次强调,不要让不应该发生的事情做出反应,而是不要让它发生。并抛出(例如)throw new InvalidPathException();

我的建议:

String loadSuperImportantFile() {
try {
return fileContent;
} catch(IOException ex) {
throw new ImportantFileMissingException("Failed to load super important file. Please check path.");
}
}
class ImportantFileMissingException extends RuntimeException {}

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通常是个坏主意。一个更好的方法是抛出一个自定义异常,该异常在调用堆栈的底部/附近捕获。。。在应用程序的控制线程上。这样,您就可以将管理(受控)应用程序出口的所有代码和逻辑放在同一个位置。

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

为什么不在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
throw new AssertionError("Unreachable");
}
}

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

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

通过这种方式,您可以避免返回null,而是返回Optional.empty(),以避免空检查和可能的NullPointerException

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

相关内容

  • 没有找到相关文章

最新更新