Java:如何高效地检查空指针
有一些模式用于检查方法的参数是否已被赋予null
值。
首先是经典之作。 这在自制代码中很常见,而且很明显。
public void method1(String arg) {
if (arg == null) {
throw new NullPointerException("arg");
}
}
其次,你可以使用现有的框架。 该代码看起来更好一些,因为它只占用一行。 缺点是它可能会调用另一种方法,这可能会导致代码运行速度稍慢,具体取决于编译器。
public void method2(String arg) {
Assert.notNull(arg, "arg");
}
第三,你可以尝试调用一个没有副作用的方法。 这可能起初看起来很奇怪,但它比上述版本少了令牌。
public void method3(String arg) {
arg.getClass();
}
我还没有看到广泛使用的第三种模式,感觉好像我自己发明了它。 我喜欢它的简短性,并且因为编译器有很好的机会将其完全优化或将其转换为单个机器指令。 我还使用行号信息编译我的代码,所以如果抛出NullPointerException
,我可以将其追溯到确切的变量,因为每行只有一个这样的检查。
你喜欢哪张支票,为什么?
方法#3: arg.getClass();
是聪明的,但除非这个习语被广泛接受,否则我更喜欢更清晰,更详细的方法,而不是保存几个字符。 我是一个“写一次,读很多”的程序员。
其他方法是自我记录:您可以使用日志消息来阐明发生了什么 - 该日志消息在读取代码时以及在运行时使用。 arg.getClass()
,现在不是自我记录。 你至少可以使用评论来澄清代码的评论者:
arg.getClass(); // null check
但是您仍然没有机会像使用其他方法一样在运行时中放入特定的消息。
方法#1和#2(null-check + NPE / IAE vs断言):我尝试按照这样的指导:
http://data.opengeo.org/GEOT-290810-1755-708.pdf
使用assert
检查私有方法的参数
assert param > 0;
使用null check + IllegalArgumentException
检查公共方法的参数
if (param == null) throw new IllegalArgumentException("param cannot be null");
在需要的地方使用null check + NullPointerException
if (getChild() == null) throw new NullPointerException("node must have children");
然而 ,因为这可能是最有效地捕获潜在null
问题的问题,所以我不得不提及我处理null
首选方法是使用静态分析,例如类型注释(例如@NonNull
)和JSR-305 。 我最喜欢的检查工具是:
Checker框架:
Java的自定义可插入类型
https://checkerframework.org/manual/#checker-guarantees
如果它是我的项目(例如不是具有公共API的库),并且我可以在整个过程中使用Checker Framework:
我可以更清楚地在API中记录我的意图(例如,此参数可能不是null(缺省值),但是这个参数可能为null( @Nullable
;方法可能返回null;等等)。这个注释在声明中是正确的,而不是远离Javadoc,所以更有可能被维护。
静态分析比任何运行时检查更有效
静态分析将提前标记潜在的逻辑缺陷(例如,我尝试将一个可能为null的变量传递给仅接受非null参数的方法),而不是依赖于运行时发生的问题。
另一个好处是,该工具让我可以将注释放在注释中(例如`/ @ Nullable /),所以我的库代码可以兼容带有类型注释的项目和非注释类型的项目(不是我有任何这些项目)。
如果链接再次失效 ,请参阅GeoTools开发人员指南中的部分:
http://data.opengeo.org/GEOT-290810-1755-708.pdf
5.1.7使用断言,IllegalArgumentException和NPE
Java语言现在已经有了一个断言关键字可用; 此关键字可用于执行仅调试检查。 虽然这种设施有几种用途,但常见的一种是在私有(不是公用)方法上检查方法参数。 其他用途是后置条件和不变量。
参考:使用断言进行编程
前提条件(如私有方法中的参数检查)通常是断言的简单目标。 后条件和不变量有时不那么直接,但更有价值,因为非平凡条件有更多的风险被打破。
使用Assert检查私有方法的参数
private double scale( int scaleDenominator ){
assert scaleDenominator > 0;
return 1 / (double) scaleDenominator;
}
您可以使用以下命令行参数启用断言:
java -ea MyApp
您只能使用以下命令行参数来关闭GeoTools断言:
java -ea:org.geotools MyApp
您可以禁用特定软件包的声明,如下所示:
java -ea:org.geotools -da:org.geotools.referencing MyApp
使用IllegalArgumentExceptions检查公共方法的参数
严格禁止在公共方法中使用断言; 因为所报告的错误是在客户端代码中完成的 - 说实话,当他们搞砸了时,会提前告诉他们IllegalArgumentException。
public double toScale( int scaleDenominator ){
if( scaleDenominator > 0 ){
throw new IllegalArgumentException( "scaleDenominator must be greater than 0");
}
return 1 / (double) scaleDenominator;
}
在需要的地方使用NullPointerException
如果可能,请执行您自己的空检查; 抛出IllegalArgumentException或NullPointerException,并提供有关出错的详细信息。
public double toScale( Integer scaleDenominator ){
if( scaleDenominator == null ){
throw new NullPointerException( "scaleDenominator must be provided");
}
if( scaleDenominator > 0 ){
throw new IllegalArgumentException( "scaleDenominator must be greater than 0");
}
return 1 / (double) scaleDenominator;
}
你不是过早地优化biiiiiiiiiiiiii!!
我只会用第一个。 它清晰简洁。
我很少和Java一起工作,但我认为有一种方法可以使Assert只在调试版本上运行,所以这是一个禁忌。
第三个让我感到毛骨悚然,如果我在代码中看到它,我想我会立即诉诸暴力。 它完全不清楚它在做什么。
你不应该抛出NullPointerException。 如果你想要一个NullPointerException,只是不检查该值,并且当参数为null并且你试图对其进行解引用时它会被自动抛出。
查看apache commons lang Validate和StringUtils类。
Validate.notNull(variable)
如果“变量”为空,它将抛出IllegalArgumentException。
如果“变量”为空(空或零长度Validate.notEmpty(variable)
将抛出IllegalArgumentException。
也许更好:
String trimmedValue = StringUtils.trimToEmpty(variable)
将保证“trimmedValue”不会为空。 如果“变量”为空,“trimmedValue”将是空字符串(“”)。
上一篇: Java: How to check for null pointers efficiently
下一篇: Is there a basic Java Set implementation that does not permit nulls?