实际开发中会遇到一个大对象,链式的处理数据,并且还不确定是否为空,如果是简单的对象嵌套判断可以直接就 == null, 但是如果还有一系列的逻辑处理,那么新特性Optional一定能更优雅简洁的实现
- Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
- Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
- Optional 类的引入很好的解决空指针异常。
Optional的作用:
Optional作为npe(NullPointerException)的解决方案,它的最主要作用就是保证操作一个null对象时,流程依旧会流转下去,不会不经开发者同意就向上抛出npe(是否抛取决于开发者的业务)。可以把这种功能简单的理解为Optional的每一步操作都会进行一个非空校验,空和非空会各自执行一个分支语句。
注意事项:
- 如果已知对象不可能为空,则尽量不要使用这个类来增加代码的复杂度。
- 如果条件判断比较简单,一般也不会用到Optional。
- 鉴于大多数情况下,开发者都不会主动去校验对象是否为null,从而导致程序中断,所以一般推荐将Optional用于查询结果的非空校验。
1. if-else操作
是Optional实际运用的基础操作,自然地,代码里的任意if-else非空判断都能与Optional相互替代。
注意事项:
当orElse通过方法来获取默认值时,orElse中的方法将总是会被执行(因为该方法接受的是一个具体值,为了获取该具体值,总是会在调用orElse方法前执行其参数中的方法)
orElseGet则不同,它只在值为空时才会执行该方法(它的参数是一个方法引用,而非具体值)
如果只是需要一个固定的默认值,可以使用orElse;当需要方法或数据库执行结果时,都推荐使用orElseGet。
Optional写法:
public static Product selectProductOtherwiseDefault(Long productId) {
Optional<Product> optional = Optional.ofNullable(getProduct(productId));
optional.ifPresent(OptionalUsage::setProductInfo);
return optional.orElse(getDefaultProduct());
// return optional.orElseThrow(BusinessException::new);
// return optional.orElseGet(OptionalUsage::getDefaultProduct);
}
if-else写法:
public static Product selectProductOtherwiseDefault1(Long productId) {
Product product = getProduct(productId);
if (product != null) {
setProductInfo(product);
} else {
product = getDefaultProduct();
// throw new BusinessException();
}
return product;
}
2. 对象属性非空校验
如果要获取对象的属性,我们不知道这个对象是否为null,以及它的属性是否为null时,可以使用Optional来代替那些非空校验。
注意事项:
map方法映射的结果仍然会被Optional封装,后续仍能对该方法返回的null值进行操作
flatMap则是直接返回的映射结果,如果结果为null,该方法会抛出npe
Optional写法:
public static List<ProductPicture> getProductPicture(Product product) {
Optional<Product> optional = Optional.ofNullable(product);
return optional
.map(Product::getProductDetail)
.map(ProductDetail::getProductPictures)
.orElse(null);
}
非空校验写法:
public static List<ProductPicture> getProductPicture1(Product product) {
if (product != null) {
ProductDetail productDetail = product.getProductDetail();
if (productDetail != null) {
return productDetail.getProductPictures();
}
}
return null;
}
对象层级越多,这种写法就越显得臃肿、越难于阅读。
3. 嵌套非空校验
- 多个对象嵌套,在不清楚某个对象是否为null时,想对某个嵌套对象做某些操作,就可以用到Optional。
Optional写法:
public static void setProductInfo(Product product) {
Optional<Product> optional = Optional.ofNullable(product);
optional.ifPresent(product1 -> {
// getBrand一般代表其它方法的结果或sql查询结果
product1.setBrand(Optional.ofNullable(product1.getBrandId()).map(OptionalUsage::getBrand).orElse(null));
product1.setCategory(Optional.ofNullable(product1.getCategoryId()).map(OptionalUsage::getCategory).orElse(null));
});
}
非空校验写法:
public static void setProductInfo1(Product product) {
if (product != null) {
Long brandId = product.getBrandId();
Long categoryId = product.getCategoryId();
if (brandId != null) {
product.setBrand(OptionalUsage.getBrand(brandId));
}
if (categoryId != null) {
product.setCategory(OptionalUsage.getCategory(categoryId));
}
}
}
这种写法很常见,但是大多数开发者都可能会忘记做这一部分的非空校验,从而导致下游服务或方法抛出npe异常,从而中断整个程序的执行。
4. 其它校验与非空校验组合
其实大部分情况下,我们除了校验对象是否为空,还会对它的某个属性进行校验,从而来组成一个完整的if-else组合。如果Optional仅支持非空校验,那么使用场景其实有限的,索性Optional还支持非空校验外的其它校验来过滤数据。
Optional写法:
通过校验后,仍然可以像前几列一样执行if-else操作
public static boolean checkProductIntegrityThenUpdate(Product product) {
Optional<Product> optional = Optional.ofNullable(product)
.filter(product1 -> null != product.getProductDetail() && null != product.getCategory() && anyOtherCheck());
optional.ifPresent(OptionalUsage::updateIntegrityProductInfo);
return optional.isPresent();
}
组合校验写法:
public static boolean checkProductIntegrityThenUpdate1(Product product) {
boolean check = product != null && null != product.getProductDetail() && null != product.getCategory() && anyOtherCheck();
if (check) {
updateIntegrityProductInfo(product);
}
return check;
}