Java中如何避免空值检查

Java中如何避免空值检查

技术背景

在Java开发中,初级到中级开发者常常会面临空值检查的问题。他们可能不了解或不信任所参与的契约,从而过度进行空值检查。此外,在编写自己的代码时,也倾向于返回空值来表示某些情况,这就要求调用者进行空值检查。空值检查会使代码变得冗长且难以维护,因此需要寻找有效的方法来避免不必要的空值检查。

实现步骤

当空值不是有效响应时

从Java 1.7开始,可以使用Objects.requireNonNull(foo)方法。该方法会返回传入的对象,如果对象为空则抛出NullPointerException。示例代码如下:

1
2
3
public Foo(Bar bar) {
this.bar = Objects.requireNonNull(bar);
}

也可以像使用断言一样使用该方法,并添加自定义的异常信息:

1
2
Objects.requireNonNull(someobject, "if someobject is null then something is wrong");
someobject.doCalc();

在Java 1.7之前,可以使用assert语句作为替代方案。

当空值是有效响应时

  • 对于返回集合的方法:尽量返回空集合(或数组)而不是空值。
  • 对于非集合类型:可以使用Null Object模式。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface Action {
void doSomething();
}

public interface Parser {
Action findAction(String userInput);
}

public class MyParser implements Parser {
private static Action DO_NOTHING = new Action() {
public void doSomething() { /* do nothing */ }
};

public Action findAction(String userInput) {
// ...
if ( /* we can't find any actions */ ) {
return DO_NOTHING;
}
}
}

使用注解

在一些Java IDE(如JetBrains IntelliJ IDEA、Eclipse、Netbeans)或工具(如findbugs)中,可以使用@Nullable@NotNull注解。示例如下:

1
2
3
4
5
6
7
@NotNull public static String helloWorld() {
return "Hello World";
}

@Nullable public static String helloWorld() {
return "Hello World";
}

使用Java 8的Optional类

Java 8引入了java.util.Optional类,它可以提高代码的可读性,并使公共API的契约更加清晰。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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();
}

Optional<Fruit> found = find("lemon", fruits);
if (found.isPresent()) {
Fruit fruit = found.get(); // Prefer found.orElseThrow() in Java 9+
String name = fruit.getName();
}

还可以使用maporElse方法:

1
2
3
String nameOrFallback = find("lemon", fruits)
.map(f -> f.getName())
.orElse("empty-name");

核心代码

使用Objects.requireNonNull方法

1
2
3
4
5
6
7
public class Example {
private Object obj;

public Example(Object obj) {
this.obj = Objects.requireNonNull(obj, "obj cannot be null");
}
}

使用Null Object模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public interface Action {
void doSomething();
}

public interface Parser {
Action findAction(String userInput);
}

public class MyParser implements Parser {
private static Action DO_NOTHING = new Action() {
@Override
public void doSomething() {
// do nothing
}
};

@Override
public Action findAction(String userInput) {
if (userInput == null || userInput.isEmpty()) {
return DO_NOTHING;
}
// 其他逻辑
return null;
}
}

使用Java 8的Optional类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

class Fruit {
private String name;

public Fruit(String name) {
this.name = name;
}

public String getName() {
return name;
}
}

public class OptionalExample {
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();
}

public static void main(String[] args) {
List<Fruit> fruits = new ArrayList<>();
fruits.add(new Fruit("apple"));
fruits.add(new Fruit("banana"));

Optional<Fruit> found = find("apple", fruits);
String name = found.map(Fruit::getName).orElse("not found");
System.out.println(name);
}
}

最佳实践

  • 明确契约:在设计API时,明确指出哪些方法可以返回空值,哪些不可以。可以使用注解或文档来进行说明。
  • 使用Optional类:对于可能返回空值的方法,优先考虑使用Optional类。它可以使代码更加清晰,减少空值检查的代码量。
  • 使用Null Object模式:在适当的情况下,使用Null Object模式来避免空值检查。例如,在处理找不到合适操作的情况时,可以返回一个不执行任何操作的对象。
  • 尽早失败:如果一个方法不允许传入空值,应该在方法开始时就进行检查,并抛出明确的异常,而不是在后续的代码中出现NullPointerException

常见问题

注解不被所有编译器支持

虽然一些IDE(如IntelliJ IDEA)支持@Nullable@NotNull注解,但并不是所有的编译器都支持。因此,在使用这些注解时,需要注意代码的可移植性。

使用Optional类可能会增加代码复杂度

虽然Optional类可以提高代码的可读性,但在某些情况下,使用它可能会增加代码的复杂度。例如,在嵌套的Optional对象中,处理起来可能会比较麻烦。

Null Object模式的维护问题

使用Null Object模式时,需要创建额外的对象,这可能会增加代码的维护成本。因此,在使用该模式时,需要权衡利弊。


Java中如何避免空值检查
https://119291.xyz/posts/2025-05-09.java-null-checking-avoidance/
作者
ww
发布于
2025年5月9日
许可协议