我们在写Java代码时,最长遇到的异常就是:NullPointerException。大量的null值的检测,降低了代码的可读性,而随时可能发生的NullPointerException降低了程序的健壮性。在Java 8中引入了Optional类,就是为了解决这样的问题,那么Optional怎么用?

Optional类

A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.

Optional对象可以包含或者不包含非 null 值的容器。如果值存在则 isPresent()方法会返回 true,调用 get() 方法会返回该对象。

Optional对象构造方法

Optional.of(T value),该方法通过一个非 null 的 value 来构造一个 Optional,返回的 Optional 包含了 value 这个值。对于该方法,传入的参数一定不能为 null,否则便会抛出 NullPointerException。

Optional.ofNullable(T value),该方法和 of 方法的区别在于,传入的参数可以为 null 。

Optional.empty(),该方法用来构造一个空的 Optional,即该 Optional 中不包含值 —— 其实底层实现还是 如果 Optional 中的 value 为 null 则该 Optional 为不包含值的状态,然后在 API 层面将 Optional 表现的不能包含 null 值,使得 Optional 只存在 包含值 和 不包含值 两种状态。

提取Optional对象的值

如果我们要获取User对象中的roleId属性值,常见的方式是直接获取:

1
2
3
4
String roleId = null;
if (user != null) {
roleId = user.getRoleId();
}

使用Optional中提供的map()方法可以以更简单的方式实现:

1
2
Optional<User> userOpt = Optional.ofNullable(user);
Optional<String> roleIdOpt = userOpt.map(User::getRoleId);

使用orElse()方法获取值

Optional类还包含其他方法用于获取值,这些方法分别为:

orElse():如果有值就返回,否则返回一个给定的值作为默认值;
orElseGet():与orElse()方法作用类似,区别在于生成默认值的方式不同。该方法接受一个Supplier<? extends T>函数式接口参数,用于生成默认值;
orElseThrow():与前面介绍的get()方法类似,当值为null时调用这两个方法都会抛出NullPointerException异常,区别在于该方法可以指定抛出的异常类型。
下面来看看这三个方法的具体用法:

1
2
3
4
5
6
String str = "Hello World";
Optional<String> strOpt = Optional.of(str);
String orElseResult = strOpt.orElse("Hello Beijing");
String orElseGet = strOpt.orElseGet(() -> "Hello Beijing");
String orElseThrow = strOpt.orElseThrow(
() -> new IllegalArgumentException("Argument 'str' cannot be null or blank."));

此外,Optional类还提供了一个ifPresent()方法,该方法接收一个Consumer<? super T>函数式接口,一般用于将信息打印到控制台:

1
2
Optional<String> strOpt = Optional.of("Hello World");
strOpt.ifPresent(System.out::println);

使用filter()方法过滤

filter()方法可用于判断Optional对象是否满足给定条件,一般用于条件过滤:

1
2
Optional<String> optional = Optional.of("lyn@dreamlyn.com");
optional = optional.filter(str -> str.contains("lyn"));

在上面的代码中,如果filter()方法中的Lambda表达式成立,filter()方法会返回当前Optional对象值,否则,返回一个值为空的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
37
38
39
40
41
42
43
44
45
46
package getstarted.features.optional;
import junit.framework.TestCase;
import java.util.Optional;
public class OptionalTestcase extends TestCase {
public class Name {
private String name;
public Name(final String name) {
this.name = name;
}
public Optional<String> getName() {
return Optional.ofNullable(name);
}
}
public class Person {
private Name name;
private int age;
private String password;
public Optional<Name> getName() {
return Optional.ofNullable(name);
}
public Optional<Integer> getAge() {
return Optional.ofNullable(age);
}
public Optional<String> getPassword() {
return Optional.ofNullable(password);
}
// normal constructors and setters
Person(final String name, final int age) {
this.name = new Name(name);
this.age = age;
}
}
public void testIfPresent() {
Optional<String> opt = Optional.of("hello");
opt.ifPresent(System.out::println);
}
public void testFlatMap() {
Person person = new Person("john", 26);
Optional<Person> personOptional = Optional.of(person);
String name = personOptional
.flatMap(Person::getName)
.flatMap(Name::getName)
.orElse("");
assertEquals("john", name);
}
}

使用场景

So, there are solutions to avoid using Optionals as method parameters. The intent of Java when releasing Optional was to use it as a return type, thus indicating that a method could return an empty value. As a matter of fact, the practice of using Optional as a method parameter is even discouraged by some code inspectors.

简而言之,使用Optional对象作为函数的返回值,而不要使用它作为函数的参数。

orElseGet 和 orElse的区别
orElseGet中的函数并不是每次都执行,只有Optional包含的对象是null时执行,而使用 orElse则其中的代码每次都执行。

不好的代码

1
AgentUser agentUser = agentUserRes.findOne(xxx).orElse(new AgentUser());

更好的代码

1
AgentUser agentUser = agentUserRes.findOne(xxx).orElseGet(() -> (new AgentUser()));

所以,在使用时,应优先采用 orElseGet。