一直以来都觉得Java引入的Optional
相当鸡肋。虽说目的是为了避免返回null
造成NPE,但是由于大量现存的代码没有采用函数式风格,加上有些程序员对这个特性的理解不足。往往看到一些一言难尽的代码。
比如:
Optional<String> optional = Optional.ofNullable(getSomething());
optional.ifPresent( s -> {
System.out.println(s);
});
或是
Optional<String> optional = Optional.ofNullable(getSomething());
if (optional.isPresent()) {
System.out.println(optional.get());
}
面对这种代码很难不让人产生何必多此一举的想法。
今天偶然发现在一个场景中Optional还是有点用处的。
首先先造一个容易产生NPE的结构。
这个结构本身没什么含义,就是为了方便写出a.b.c.d
这样的代码。
注意:这种代码风格是非常不好的风格,违反了迪米特法则。容易NPE是不良设计附带产生的问题,所以如果能重构消除这种代码是更好的解决方案。
public static class Wrap<T> {
T child;
public Wrap(T child) {
this.child = child;
}
static <T> Wrap<T> of(T child) {
return new Wrap<>(child);
}
}
有了这个递归的结构,就可以造出任意层嵌套的结构。
Wrap<Wrap<Wrap<String>>> wrap1 = Wrap.of(Wrap.of(Wrap.of("A")));
Wrap<Wrap<Wrap<String>>> wrap2 = Wrap.of(Wrap.of(null));
Wrap<Wrap<Wrap<String>>> wrap3 = Wrap.of(null);
如果客户代码从最外层依次访问这个结构(违反信息隐藏原则),而结构中的任何一层都有为null
的可能性。那就很容易出现NPE。
比如
assertEquals(wrap2.child.child.child, null); //throw NPE
要安全的处理也相当的繁琐。
if (wrap2.child != null) {
if (wrap2.child.child != null) {
...
}
}
这里可以用Optional
相对简洁的处理这个问题。
String s = Optional.ofNullable(wrap2)
.map(w -> w.child)
.map(w -> w.child)
.map(w -> w.child)
.orElse("");
assertEquals(s, "");
简单来说就是:
- 不要急着从
Optional
中get
出值来,让FP代码飞一会 - 使用
map
来处理Optional
中包装的值,而不要采用ifPresent
这样产生副作用的操作 -
map
可以连续使用,每次向下一层,就可以不必在每层处理null
的判断