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
public class A {

private B b;

public B getB() {
return b;
}

public void setB(B b) {
this.b = b;
}
}

public class B {

private String bName;
private String bAge;

public String getbName() {
return bName;
}

public void setbName(String bName) {
this.bName = bName;
}

public String getbAge() {
return bAge;
}

public void setbAge(String bAge) {
this.bAge = bAge;
}
}

已经存在两个类,并且互相嵌套,而且不允许修改, A 、B 两个类的结构,我们要安全的访问 A B中的值,尽可能少的使用 if 语句。

1
2
3
4
5
A a = null;
Optional<A> a1 = Optional.ofNullable(a);
Optional<B> b = a1.filter((t) -> t.getB() != null).map(A::getB);
// 上面一行代码的问题是, t 有可能会为 null ,从而引发 NPE
B b2 = b.get();
1
2
3
4
5
6
Optional<A> a1 = Optional.ofNullable(a);
Optional<B> b = a1.map(A::getB);
b.get(); // 有可能会抛异常,因为如果 b 为 null,那么得到的 Optional 为 empty 创建的
b.orElse(new B()); // 没有问题,返回 new B 的对象
b.orElseGet(() -> B :: new); // 没有问题,返回 new B 的对象
b.orElseThrow(() -> YdException::new); // 手动抛出异常

使用 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
public class PersonNoOptional {

private Car car;

public Car getCar() {
return car;
}

public void setCar(Car car) {
this.car = car;
}

public static class Car{
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
}

上面的代码是没有使用 Optional 时候,我们经常会写的样式。会出现的问题:

  • 如果某个值为 null,立马会报出 NPE

我们的解决方式

1
2
3
4
5
6
7
8
9
10
11
12
public class OptionService {

public void opt() {
PersonNoOptional p = new PersonNoOptional();

PersonNoOptional.Car car = p.getCar();
if (car != null) {
// ....
}
}

}

会添加很多的 if 来进行判断,甚至还有空对象设计模式(Null Object Pattern) 来处理这一类的问题。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
public class PersonOptional {
private Optional<PersonNoOptional.Car> car;

public Optional<PersonNoOptional.Car> getCar() {
return car;
}

public void setCar(PersonNoOptional.Car car) {
this.car = Optional.of(car);
}

public static class Car {
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
}

Peron 有可能会没有 Car,但是每一辆 Car 都必须有 name,所以我们对 Car 使用了 Optional 包装,而 name 没有使用 Optional 的原因就在这里。

Optional 的创建

empty

创建一个空的 Optional 对象

1
Optional<Object> empty = Optional.empty();

empty() 方法的实现

1
2
3
4
5
6
7
8
9
private static final Optional<?> EMPTY = new Optional<>();

private Optional() { this.value = null; }

public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}

of

1
Optional<B> optionalB = Optional.of(new B());

of 方法中的参数如果为 null,会发生 NPE

1
Optional<Object> optional = Optional.of(null);

of() 方法的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Objects.requireNonNull 的实现
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}

private Optional(T value) {
this.value = Objects.requireNonNull(value);
}

public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}

ofNullable

ofNullable 允许传入的参数为 null

1
2
A a = null;
Optional<A> optonal = Optional.ofNullable(a);

ofNullable 的实现

1
2
3
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}

#Optinal 中获取值

get

1
A a = optionalA.get();

如果 Optional 容器中不存在值,会抛出异常 NoSuchElementException("No value present")

get 的实现

1
2
3
4
5
6
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}

orElse

1
A a = optionalA.orElse(new A());

如果 Optional 容器中不存在值,使用 orElse 方法中定义的值。

orElse 的实现

1
2
3
public T orElse(T other) {
return value != null ? value : other;
}

orElseGet

1
A a = optionalA.orElseGet(A::new);

如果 Optional 容器中不存在值,会执行定义的函数。

orElseGet 的实现

1
2
3
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}

orElseThrow

1
A a = optionalA.orElseThrow(RuntimeException::new);

如果 Optional 容器中不存在值,会抛出指定的异常。与 get 方法的区别是,get 方法抛出的异常为固定的,该方法可以抛出指定的异常。

orElseThrow 的实现

1
2
3
4
5
6
7
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}

Optional 容器中的值为空时,使用了 throw 关键字。

map 和 flatMap

map

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
public class A {
private B b;
public B getB() {
return b;
}
}

public class B {
private Name bName;
private String bAge;
public Name getbName() {
return bName;
}

public void setbName(Name bName) {
this.bName = bName;
}

public String getbAge() {
return bAge;
}

public void setbAge(String bAge) {
this.bAge = bAge;
}

public static class Name{
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
}

A B 两个类的结构关系是互相嵌套,我们要取出 b.Name.getName() 的值

1
2
3
4
Optional<String> aName = optionalA.map(A::getB)
.map(B::getbName)
.map(B.Name::getName);
System.out.println(aName.orElse("kkk"));

flatMap

如果 B 在 A 的嵌套中,使用了 Optional 包装

1
2
3
4
5
6
7
8
public class A {

private Optional<B> b;

public Optional<B> getB() {
return b;
}
}

再使用上面的访问,就会编译报错。

原因:

1
Optional<Optional<B>> optional = optionalA.map(A::getB);

map 的返回外面被包装了一层 Optional ,想要达到上面的效果,需要拆掉一层 Optional 的包装,那么此时就可以使用 flatMap 来打散一层 Optional 的包装

1
2
3
4
String kkk = optionalA.flatMap(A::getB)
.map(B::getbName)
.map(B.Name::getName)
.orElse("kkk");

ypxh就可以顺利访问了

map 和 flatMap 的区别在于,flatMap 会进行拆包(将外面的层包装拆除)的动作,而 map 不会进行拆包

Optional 提供的其他方法

isPresent

isPresent 用于判断 Optional 容器中值是否为空(null),不为空返回会 true,空返回 false

1
2
3
public boolean isPresent() {
return value != null;
}

ifPresent

ifPresent 提供了执行函数式代码的能力,当 Optional 容器中的值不为空时,会执行传入的函数式代码。

1
optionalA.ifPresent(c -> System.out.println(c.getB()));

ifPresent 的实现

1
2
3
4
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}

filter

通过执行传入的谓词 进行过滤,如果传入的 谓词 执行结果为 true 返回 Optional 容器本身,否则返回空容器。

1
2
3
4
5
6
7
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}