我使用x!=null以避免NullPointerException。还有其他选择吗?
if (x != null) {
// ...
}
我使用x!=null以避免NullPointerException。还有其他选择吗?
if (x != null) {
// ...
}
当前回答
我遵循以下准则以避免空检查。
尽可能避免成员变量的延迟初始化。初始化声明本身中的变量。这将处理NullPointerExceptions。在周期早期决定成员变量的可变性。有效地使用像final关键字这样的语言结构。如果您知道不会更改方法的扩充,请将它们声明为final。尽可能限制数据的变异。某些变量可以在构造函数中创建,并且永远无法更改。删除公共setter方法,除非确实需要它们。例如,假设应用程序中的一个类(A.java)正在维护HashMap这样的集合。不要在A.java中提供公共getter方法,而允许B.java直接在Map中添加元素。而是在A.java中提供一个API,它将一个元素添加到集合中。//避免a.getMap().put(键,值)//推荐的public void addElement(对象键,对象值){//在此处对键和值进行空检查:单个位置map.put(键,值);}最后,在正确的位置有效地使用try{}catch{}finally{}块。
其他回答
您还可以使用CheckerFramework(带有JDK7及更高版本)静态检查空值。这可能会解决很多问题,但需要运行一个目前仅适用于OpenJDK AFAIK的额外工具。https://checkerframework.org/
Java8带来了新的Java.util.Optional类,它可以解决一些问题。至少可以说,它提高了代码的可读性,并且在公共API的情况下,使API的契约对客户端开发人员更加清晰。
它们是这样工作的:
给定类型(Fruit)的可选对象被创建为方法的返回类型。它可以是空的或包含Fruit对象:
public static Optional<Fruit> find(String name, List<Fruit> fruits) {
for (Fruit fruit : fruits) {
if (fruit.getName().equals(name)) {
return Optional.of(fruit);
}
}
return Optional.empty();
}
现在看看这段代码,我们在其中搜索给定Fruit实例的Fruit(水果)列表:
Optional<Fruit> found = find("lemon", fruits);
if (found.isPresent()) {
Fruit fruit = found.get();
String name = fruit.getName();
}
您可以使用map()运算符对可选对象执行计算,或从中提取值。orElse()允许您为缺少的值提供回退。
String nameOrNull = find("lemon", fruits)
.map(f -> f.getName())
.orElse("empty-name");
当然,检查空/空值仍然是必要的,但至少开发人员意识到该值可能是空的,忘记检查的风险是有限的。
在从头开始使用Optional构建的API中,只要返回值可能为空,并且仅在不能为空时返回纯对象(惯例),客户端代码可能会放弃对简单对象返回值的空检查。。。
当然,Optional也可以用作方法参数,在某些情况下,可能比5或10个重载方法更好地指示可选参数。
可选提供了其他方便的方法,例如允许使用默认值的orElse,以及与lambda表达式一起使用的ifPresent。
我邀请您阅读这篇文章(我撰写这个答案的主要来源),其中很好地解释了NullPointerException(以及一般的空指针)问题以及Optional带来的(部分)解决方案:Java Optional Objects。
Guava是Google提供的一个非常有用的核心库,它有一个很好且有用的API来避免空值。我发现UsingAndAvoidingNullExplain非常有用。
正如wiki中所解释的:
可选<T>是用非空值。可选可以包含非空T引用(在这种情况下,我们称引用为“present”),或者它可能包含什么都没有(在这种情况下,我们说引用“不存在”)。从来没有称为“包含null”
用法:
Optional<Integer> possible = Optional.of(5);
possible.isPresent(); // returns true
possible.get(); // returns 5
可以在方法调用之前使用拦截器。这就是面向方面编程所关注的。
假设M1(对象测试)是一个方法,M2是一个在方法调用之前应用方面的方法,M2(对象测试2)。如果test2!=null则调用M1,否则执行另一件事。它适用于所有要应用方面的方法。如果要为实例字段和构造函数应用方面,可以使用AspectJ。对于方法方面,Spring也是最佳选择。
切勿将变量初始化为空。如果(1)不可能,则将所有集合和数组初始化为空集合/数组。
在您自己的代码中执行此操作,您可以避免!=空检查。
大多数时候,空检查似乎保护了集合或数组上的循环,所以只要将它们初始化为空,就不需要任何空检查。
// Bad
ArrayList<String> lemmings;
String[] names;
void checkLemmings() {
if (lemmings != null) for(lemming: lemmings) {
// do something
}
}
// Good
ArrayList<String> lemmings = new ArrayList<String>();
String[] names = {};
void checkLemmings() {
for(lemming: lemmings) {
// do something
}
}
这是一个很小的开销,但它值得更干净的代码和更少的NullPointerExceptions。