我经常使用object != null来避免NullPointerException 。
有没有好的替代方法?
例如:
if (someobject != null) {
someobject.doCalc();
}
如果不知道对象是否为null ,则可以避免NullPointerException 。
请注意,接受的答案可能已过期,请参阅https://stackoverflow.com/a/2386013/12943以获取最新的方法。
#1楼
Java 7有一个新的java.util.Objects实用程序类,在该类上有一个requireNonNull()方法。 如果它的参数为null,则所有操作都将引发NullPointerException ,但会稍微清理一下代码。 例:
Objects.requireNonNull(someObject);
someObject.doCalc();
该方法对于检查构造函数中的赋值之前最有用,其中每次使用它可以节省三行代码:
Parent(Child child) {
if (child == null) {
throw new NullPointerException("child");
}
this.child = child;
}
变成
Parent(Child child) {
this.child = Objects.requireNonNull(child, "child");
}
#2楼
最终,完全解决此问题的唯一方法是使用另一种编程语言:
- 在Objective-C中,您可以等效于在
nil上调用方法,并且绝对不会发生任何事情。 这使得大多数null检查都是不必要的,但会使错误更难以诊断。 - 在Java派生的语言Nice中 ,有两种所有类型的版本:可能为空的版本和非为空的版本。 您只能在非空类型上调用方法。 通过显式检查null,可以将潜在为null的类型转换为非null的类型。 这样可以更轻松地知道哪些地方需要空检查,哪些地方没有空检查。
#3楼
只是永远不要使用null。 不要这样
在我的课程中,大多数字段和局部变量都具有非null的默认值,并且我在代码的各处添加了合同声明(始终为断言),以确保这是强制执行的(因为它更简洁,比让它更具表达力)作为NPE出现,然后必须解析行号等)。
一旦采用了这种做法,我发现问题似乎已经解决。 您会在开发过程中很早就偶然发现问题,并且意识到自己的弱点。更重要的是,它有助于封装不同模块的关注点,不同模块可以“相互信任”,而不会产生混乱。 if = null else代码if = null else构造!
从长远来看,这是防御性的编程,可以使代码更简洁。 始终对数据进行消毒,例如在此处执行严格的标准,这样问题就会消失。
class C {
private final MyType mustBeSet;
public C(MyType mything) {
mustBeSet=Contract.notNull(mything);
}
private String name = "<unknown>";
public void setName(String s) {
name = Contract.notNull(s);
}
}
class Contract {
public static <T> T notNull(T t) { if (t == null) { throw new ContractException("argument must be non-null"); return t; }
}
合同就像微型单元测试一样,即使在生产中也总是运行,并且当发生故障时,您知道为什么,而不是随机的NPE,您必须以某种方式弄清楚。
#4楼
Java 8附带了新的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 ( fruits )对于给定的水果如:
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中,无论何时返回值可能为空,并且仅当它不能为null (约定)时才返回普通对象,客户端代码可能会放弃对简单对象返回值的null检查...
当然, Optional也可以用作方法参数,在某些情况下,这可能是比5或10个重载方法更好的指示可选参数的方法。
Optional提供其他方便的方法,如orElse ,允许使用默认值,并ifPresent与工作lambda表达式 。
我邀请您阅读本文(我写此答案的主要来源),其中很好地解释了有问题的NullPointerException (通常是空指针)以及Optional带来的(部分)解决方案: Java Optional Objects 。
#5楼
- 切勿将变量初始化为null。
- 如果无法使用(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而言,这是值得的。