• jdk1.8新特性之Optional


    一、传统写法

    @Data
    public class SkuVO {
    
        private Long skuId;
    
        private Price price;
    
    }
    
    @Data
    public class Price {
    
        private BigDecimal mallPrice;
    
        private BigDecimal sellPrice;
    
    }
    

    有一个SKU对象,里面包含一个skuId和一个price对象,price对象里面有市场价和成本价。假如现在有个需求,获取sku里面的mallPrice,并且返回。

    毫无疑问,NPE相信每个程序员都不可能没遇到过。jdk1.8以前一般是这么写:

        private static BigDecimal fetchMallprice(SkuVO skuVO) throws Exception {
    
            if (skuVO != null) {
                Price price = skuVO.getPrice();
                if (price != null) {
                    BigDecimal mallPrice = price.getMallPrice();
                    if (mallPrice != null) {
                        if (mallPrice.compareTo(new BigDecimal("10")) == 1) {
                            return mallPrice;
                        }
                    }
                }
            }
    
            throw new Exception("skuVO不符合要求,请检查");
    
        }
    

    其实在真实项目中,这种写法实在是太普遍了,各种非空判断才敢往下执行,否则就会抛出NPE。但是这种写法if嵌套得太多了,可读性很差。

    所以我们也可以像这样,提前判断是否为空,为空则抛异常:

       private static BigDecimal fetchMallprice(SkuVO skuVO) throws Exception {
    
            if (skuVO == null) {
                throw new Exception("skuVO不符合要求,请检查");
            }
    
            Price price = skuVO.getPrice();
    
            if (price == null) {
                throw new Exception("skuVO不符合要求,请检查");
            }
    
            BigDecimal mallPrice = price.getMallPrice();
            if (mallPrice != null) {
                if (mallPrice.compareTo(new BigDecimal("10")) == 1) {
                    return mallPrice;
                }
            }
    
            throw new Exception("skuVO不符合要求,请检查");
        }
    

    虽然嵌套减少了,但是还是比较臃肿的。JDK1.8出来后,我们就可以优雅地写这种功能了。

    二、JDK1.8写法

        private static BigDecimal fetchMallprice(SkuVO skuVO) throws Exception {
    
            return Optional.ofNullable(skuVO)
                    .map(s -> s.getPrice())
                    .map(p -> p.getMallPrice())
                    .filter(m -> m.compareTo(new BigDecimal("10")) == 1)
                    .orElseThrow(() -> new Exception("skuVO不合法"));
    
        }
    

    Optional是jdk1.8出的新特性,其实思想挺简单的,就是把实体包装了一层,包装的时候,如果是实体为空,则返回一个空的Optional,否则返回Optional

    Optional.ofNullable(skuVO)首先构造一个Optional,map(Function<? super T, ? extends U> mapper)接收一个Funtion,filter过滤map返回的值,最后通过orElseThrow抛出异常。

    三、源码分析

    3.1 构造方法

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

    Optional的构造方法都是private,所以其提供了三个静态public方法来构造Optional:

    private static final Optional<?> EMPTY = new Optional<>();
    
    private final T value;
    
    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }
    
    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }
    
    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }
    

    EMPTY是当Optional为空的时候返回的。

    T是实际值。

    empty()方法返回一个空实例EMPTY,即Optional里面不包含值,那么我们使得 Optional 只存在 包含值 和 不包含值 两种状态。

    of(T value)方法将新建一个非空的Optional,如果value为空,那么会抛出NPE。

    ofNullable(T value)方法里的value可以为空,如果为空,则调用empty()方法空的Optional,否则调用of(T value)方法一个非空的Optional。

    3.2 ifPresent

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

    如果当前Optional不为空,则调用consumer.accept(value)方法,而Consumer又是函数式接口,故利用lambda表达式可以这样写:

    Optional<SkuVo> sku = Optional.ofNullable(getSku());
    sku.ifPresent(s -> System.out.println(s.getSkuId()));
    
    

    3.3 orElse

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

    如果Optional不为空,则返回,否则执行orElse传入的参数。

    3.4 orElseGet

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

    如果Optional不为空,则返回,否则返回Supplier实现类的值,Supplier也是函数式接口,里面只有get()方法。

    3.5 orElseThrow

    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }
    

    同orElseGet,只是但当Optional为空的时候,会抛出异常,抛出的异常由传入的异常exceptionSupplier提供。

    3.6 map

    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }
    

    当Optional为空的时候,返回empty(),否则返回一个新的Optional,该Optional包装的是mapper以value作为输入的输出值。

    3.7 flatMap

    public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }
    

    flatMap和map方法的区别是,map方法参数中的mapper输出的是值,map方法会使用Optional.ofNullable将其包装成Optional,而flatMap要求参数中的mapper输出的就是Optional。

    3.8 filter

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

    filter方法接受一个Predicate来对Optional包含的值进行过滤,如果包含的值满足条件,那么还是返回这个Optional,否则返回empty()。

  • 相关阅读:
    CSharpThinkingC# 要点(附加三)
    CSharpThinkingC#3 革新(附加二)
    CSharpThinking委托相关(二)
    C++之this指针与另一种“多态”
    《C++应用程序性能优化::第二章C++语言特性的性能分析》学习和理解
    《C++应用程序性能优化::第一章C++对象模型》学习和理解
    回答总结:C实现“动态绑定”
    编译器对临时变量的优化简单理解
    虚函数表里边保存的不一定是虚函数的地址
    C++对象内存布局测试总结
  • 原文地址:https://www.cnblogs.com/kobelieve/p/11265651.html
Copyright © 2020-2023  润新知