这个问题与这个问题相似,但重点有所不同
我有一个函数,可以在磁盘上的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中不是终止语句(与return
或throw
类似)。
在一些有线的情况下,您可以想象,可以覆盖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