目录
3.1 不要直接返回 null,使用 Optional.empty();
3.3 少用 get(),多用orElse()和orElseGet()
一 Optional 是什么
Optional 的作者 Brian Goetz 对这个 API 的说明:
Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result", and using null for such was overwhelmingly likely to cause errors.
为了避免 null 带来的错误,我们提供了一个可以明确表示空值的有限的机制。
Optional是一个容器,用于放置可能为空的值,它可以合理而优雅的处理null。
二 Optional API介绍
public static<T> Optional<T> empty(); // 返回一个Optional容器对象
public static <T> Optional<T> of(T value); // 创建一个Optional对象,如果 value 是 null,则抛出 NPE
public static <T> Optional<T> ofNullable(T value); // 创建一个Optional对象,但 value 为空时返回Optional.empty()
public T get(); // 返回Optional中包装的值,在判空之前,千万不要直接使用
public boolean isPresent(); // 判断Optional中是否有值,返回 boolean
public void ifPresent(Consumer<? super T> consumer); // 判断Optional中是否有值,有值则执行 consumer,否则什么都不干
public T orElse(T other); // 返回Optional中包装的值,但不同的是当取不到值时,返回你指定的 default
public T orElseGet(Supplier<? extends T> other); // 返回Optional中包装的值,取不到值时,返回你指定的 default
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier); // 返回Optional中包装的值,取不到值时抛出指定的异常
三 Optional 最佳实践
Optional 属于返回类型,通常在业务返回值或者远程调用中使用。
3.1 不要直接返回 null,使用 Optional.empty();
// 使用Optional.empty()之前
private PoiTabHintDTO getPoiTabHintFromTask(FutureTask<PoiTabHintDTO> task) {
if (Objects.isNull(task)) {
return null;
}
try {
return ThreadPoolUtils.get(task, 4, TimeUnit.SECONDS);
} catch (Exception e) {
log.error("getRdcPoiTabHintFromTask", e);
Cat.logMetricForCount("getRdcPoiTabHintFromTask");
return null;
}
}
// 使用Optional.empty()
private Optional<PoiTabHintDTO> getPoiTabHintFromTask(FutureTask<Optional<PoiTabHintDTO>> task) {
if (Objects.isNull(task)) {
return Optional.empty();
}
try {
return ThreadPoolUtils.get(task, 4, TimeUnit.SECONDS);
} catch (Exception e) {
log.error("getRdcPoiTabHintFromTask", e);
Cat.logMetricForCount("getRdcPoiTabHintFromTask");
return Optional.empty();
}
}
// 需要显示的从optional容器里面获取对象,能减少NPE的产生。
3.2 正确使用 ifPresent()
// 使用 ifPresent()之前
if (getPoiTabHintFromTask(rdcPoiHintTask).isPresent()) {
poiTabHintList.add(getPoiTabHintFromTask(rdcPoiHintTask).get());
}
// 使用 ifPresent()
getPoiTabHintFromTask(rdcPoiHintTask).ifPresent(poiTabHintList::add);
//性能没有提升,代码变得简洁。
3.3 少用 get(),多用orElse()和orElseGet()
// orElse()适用于入参是具体的值
public static ConfirmAuthorizationEnum getByCode(int code) {
return Arrays.stream(ConfirmAuthorizationEnum.values())
.filter(x -> code == x.getCode())
.findFirst()
.orElse(ConfirmAuthorizationEnum.UNINITIALIZED);
}
// orElseGet()适用于lamda表达式
private Optional<DeliveryReturnBillBO> getReturnBillFromList(
List<DeliveryReturnBillBO> allDeliveryReturnBillList, String returnBillNo) {
return Optional.ofNullable(allDeliveryReturnBillList.stream()
.filter(deliveryReturnBillBO -> returnBillNo.equals(deliveryReturnBillBO.getReturnBillNo()))
.findFirst()
.orElseGet(() -> {
log.error("未获取到退供单{}的详情信息", returnBillNo);
Cat.logMetricForCount(DeliveryBill.RELATED_RETURN_BILL_IS_NULL);
return null;
}));
}
// 为啥少用 get(),需要结合判空使用,这和!=null其实没多大区别。
3.3 少用 get(),多用orElse()和orElseGet()
// orElse()适用于入参是具体的值
public static ConfirmAuthorizationEnum getByCode(int code) {
return Arrays.stream(ConfirmAuthorizationEnum.values())
.filter(x -> code == x.getCode())
.findFirst()
.orElse(ConfirmAuthorizationEnum.UNINITIALIZED);
}
// orElseGet()适用于lamda表达式
private Optional<DeliveryReturnBillBO> getReturnBillFromList(
List<DeliveryReturnBillBO> allDeliveryReturnBillList, String returnBillNo) {
return Optional.ofNullable(allDeliveryReturnBillList.stream()
.filter(deliveryReturnBillBO -> returnBillNo.equals(deliveryReturnBillBO.getReturnBillNo()))
.findFirst()
.orElseGet(() -> {
log.error("未获取到退供单{}的详情信息", returnBillNo);
Cat.logMetricForCount(DeliveryBill.RELATED_RETURN_BILL_IS_NULL);
return null;
}));
}
// 为啥少用 get(),需要结合判空使用,这和!=null其实没多大区别。
3.4 少用of(),多用ofNullable()
// 使用 of()场景
if (Objects.isNull(returnBillProcessBo)) {
return Optional.empty();
}
return Optional.of(returnBillProcessBo);
// 使用 ofNullable()场景
return Optional.ofNullable(returnBillProcessBo);
四 Optional 常见用法
package com.example.demo.domain;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 功能描述
*
* @since 2022-09-23
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {
// 兴趣id列表
private List<Long> favList;
// 标签列表
private List<Tag> tagList;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class Tag {
private Long id;
}
/**
* 获取兴趣id列表
* @param user
* @return
*/
public List<Long> getFavList(User user) {
return Optional.ofNullable(user)
.map(User::getFavList)
.map(Collection::stream)
.orElse(Stream.empty())
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
/**
* 获取标签id列表
* @param user
* @return
*/
public List<Long> getTagIdList(User user) {
return Optional.ofNullable(user)
.map(User::getTagList)
.map(Collection::stream)
.orElse(Stream.empty())
.map(Tag::getId)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
}