原创:扣钉日记(微信公众号 ID:codelogs),欢送分享,转载请保留出处。
简介
要说 Java 中什么异样最容易呈现,我想 NullPointerException 肯定当仁不让,为了解决这种 null 值判断问题,Java8 中提供了一个新的工具类 Optional,用于提醒程序员留神 null 值,并在特定场景中简化代码逻辑。
比方上面一段取深层属性值的代码:
Order order = getOrderById(orderId);
String userCode = "";
if(order != null){if(order.getUser() != null){if(order.getUser().getUserCode() != null){userCode = order.getUser().getUserCode().toUpperCase();
}
}
}
这种场景还比拟常见,但深层嵌套的 if 判断,让代码阅读者压力倍增。
看看用 Optional 后的写法,如下:
Order order = getOrderById(orderId);
String userCode = Optional.ofNullable(order)
.map(Order::getUser)
.map(User::getUserCode)
.map(String::toUpperCase)
.orElse("")
链式调用的写法,让代码可读性加强了不少,不必判断 null,是因为 Optional 在外部曾经做了 null 值判断了!那咱们来看看 Optional 都有哪些用法吧。
创立 Optional
ofNullable()办法
创立一个 Optional,传入的值能够是 null 或不是 null。
of()办法
失去一个 Optional,明确晓得传入的值不是 null 时用这个,如果传 null 会报错 NullPointerExcepiton。
其实值不为 null 个别是没必要应用 Optional 的,这个应该是用于非凡场景,比方办法返回值必须是一个 Optional。
empty()办法
失去一个空的 Optional,个别也用于返回值必须是 Optional 的场景。
判空
ifPresent()办法
判断是否有值,留神,这个办法尽管看起来挺好用的,但它不太应该是应用 Optional 时第一个应用的办法,如下:
if(opt.ifPresent()){
...
}
if(obj != null) {...}
这两个代码除了写法不一样,对于代码可读性方面没有基本区别!
取值
get()办法
获取 Optional 的值,当没有值时会抛出一个 NoSuchElementException 异样。
同样的,它也不太应该是应用 Optional 时的第一个应用的办法,因为抛 NoSuchElementException 与抛 NullPointerException 并没有太大区别。
orElse 办法
没有值时会返回指定的值。
String name = nameOpt.orElse("");
orElseGet 办法
同上,不过参数变成了一个提供默认值的 lambda 函数,这用在取指定值须要肯定代价的场景,如下:
BigDecimal amount = amountOpt.orElseGet(() -> calcDefaultAmount());
orElseThrow 办法
没有值时抛出一个指定的异样,如下:
Optional<User> userOpt = getUser(userId);
User user = userOpt.orElseThrow(() -> new NullPointerException("userId:" + userId));
可能会有人疑难,你还是抛出了一个 NPE 异样,和不应用 Optional 有啥区别?区别是这个 NPE 异样会通知你哪个 userId 查不到数据,不便定位问题,而 jvm 抛出的 NPE 是没有这个信息的。
函数式解决
ifPresent(Consumer<? super T> consumer)办法
这个办法和 ifPresent()
办法不一样,这个办法代表如果 Optional 有值时,就执行传入的 lambda 函数,如下:
userOpt.ifPresent((user) -> System.out.printf("%s\n", user.toString()));
filter 办法
这个办法用于过滤 Optional 中的值,若 Optional 有值,且值满足过滤函数,则返回此 Optional,否则返回空 Optional。
String name = nameOpt.filter(StringUtils::isNotBlank).orElse("");
map 办法
这个办法用于转换值,在最后面曾经展现过了,若 Optional 有值,执行 map 中的 lambda 函数转换值,如下:
Order order = getOrderById(orderId);
String userCode = Optional.ofNullable(order)
.map(Order::getUser)
.map(User::getUserCode)
.map(String::toUpperCase)
.orElse("")
Optional 还提供了一个 flatMap 办法,与 map 办法的区别是,传给 flatMap 的 lambda 函数,这个 lambda 函数的返回值须要是 Optional。
Optional 争议点
其实到底该不该用 Optional,业界还是有不少争议的,一方面是 Optional 能强制开发者解决 null 值,但另一方面是 Optional 又非常容易滥用,特地是一些开发者拿到 Optional 之后就间接调用 get()
或ifPresent()
办法,这样简直没解决任何问题,还减轻了编码累赘。
因而,我的倡议是,在你不晓得该不该应用 Optional 的场景,那就先别用。
上面是一些应用 Optional 的场景参考,如下:
- Optional 个别用于返回值
Optional 大多用于返回值,不举荐用在成员变量或办法参数中。 - Optional 自身不判 null
永远都不要给 Optional 赋值 null,也不要判断 Optional 自身是否为 null,这是因为 Optional 原本就是解决 null 的,再引入 null 就没意思了,这应该成为业界共识。 - 汇合不应用 Optional
因为汇合有Collections.emptyList()
等更好的解决办法了,没必要再应用 Optional。 - 函数式解决值
从下面的用法介绍中就能发现,Optional 提供了很多 lambda 函数式解决的办法,如 filter、map 等,这些是应用 Optional 时比拟举荐应用的,因为 Optional 能帮你主动解决 null 值状况,防止 NPE 异样。 - 多层属性获取
后面举过这个代码样例,我感觉这是 Optional 应用收益最显著的一个场景。 -
不返回 null 胜过返回 Optional
返回 Optional 给调用方,会强制调用方解决 null 状况,会给调用方减少一些的编码累赘,特地是复用度很高的函数。
但如果调用方大多数状况下都不冀望获取到 null,那应该实现一个这样的办法,要么返回值,要么异样,如下:/** * 查问订单,要么返回订单,要么异样 */ public Order getOrderByIdOrExcept(Long orderId){Order order = orderMapper.getOrderById(orderId); if(order == null){throw new BizException("依据单号" + orderId + "未查问到订单!"); } return order; } /** * 查问订单,值可能为 null */ public Optional<Order> getOrderById(Long orderId){Order order = orderMapper.getOrderById(orderId); return Optional.ofNullable(order); }
因为前面解决代码依赖订单数据,获取不到订单数据,代码也没法往下走,所以在大多数状况下,抉择应用
getOrderByIdOrExcept
办法更好,即防止了 NPE,又防止了减少编码累赘!
总结
Optional 能解决一些问题,但因为容易滥用而争议很大,因为 Optional 将 null 的解决交给调用方了,但大多数状况下,调用方也没方法解决这个 null 状况,还不如让 JVM 抛一个 NPE 异样中止执行,因为如果你用 ifPresent 的话,还更容易造成逻辑 bug 导致执行了不该执行的代码。
这和 Java 的受查看异样是一样的,强制要求调用方解决异样,但又有多少场景的异样是调用方能够解决的呢?这导致开发人员常常滥用 catch,对异样解决一把梭了,最初发现 catch 前面还有一些本不该被执行的代码执行了。