Optional 使用详解

作者: 小波同学 | 来源:发表于2022-08-06 02:44 被阅读0次

    一、Optional类的来源

    到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。

    二、Optional 简介

    Optional 类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。

    Optional 提供很多有用的方法,能帮助我们将 Java 中的对象等一些值存入其中,这样我们就不用显式进行空值检测,使我们能够用少量的代码完成复杂的流程。

    可以说,使用 Optional 可以帮助我们解决业务中,减少值动不动就抛出空指针异常问题,也减少 null 值的判断,提高代码可读性等,这里我们介绍下,如何使用这个 Optional 类。

    三、Optional 类描述

    Optional类的Javadoc描述如下:

    • 这是一个可以为null的容器对象。
    • 如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
    • 如果值不存在则isPresent()方法会返回false,调用get()方法会NPE。

    3.1 创建Optional类对象的方法

    • Optional.of(T t):创建一个 Optional 实例,t必须非空;
    • Optional.empty():创建一个空的 Optional 实例
    • Optional.ofNullable(T t):t可以为null
    public final class Optional<T> {
    
        /**
         * 返回一个空的Optional实例。 此Optional没有值。
         *
         * 类型参数:<T> –不存在的值的类型
         * 返回值:一个空的Optional
         * api注意:尽管这样做可能很诱人,但应通过将==与Optional.empty()返回的实例进行比较来避免测试对象是否为空。
         * 不能保证它是一个单例。
         * 而是使用isPresent()
         */
        public static<T> Optional<T> empty() {
            @SuppressWarnings("unchecked")
            Optional<T> t = (Optional<T>) EMPTY;
            return t;
        }
    
        /**
         * 返回一个Optional描述给定的非null值。
         *
         * 参数:value –要描述的值,必须为非null
         * 类型参数:<T> –值的类型
         * 返回值:存在值的Optional
         */
        public static <T> Optional<T> of(T value) {
            return new Optional<>(value);
        }
    
        /**
         * 返回一个描述给定值的Optional ,如果不为null ,则返回一个空的Optional。
         * 
         * 参数:值–描述的可能为null值
         * 类型参数:<T> –值的类型
         * 返回值:一个Optional与如果指定值是非当前值null ,否则一个空Optional
         */
        public static <T> Optional<T> ofNullable(T value) {
            return value == null ? empty() : of(value);
        }
    }   
    

    3.2 Optional API 归类

    3.2.1 判断Optional容器中是否包含对象

    • boolean isPresent():判断是否包含对象
    • void ifPresent(Consumer<? super T> consumer):如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它。

    3.2.2 获取Optional容器的对象

    • T get():如果调用对象包含值,返回该值,否则抛异常
    • T orElse(T other):如果有值则将其返回,否则返回指定的other对象。
    • T orElseGet(Supplier<? extends T> other):如果有值则将其返回,否则返回由Supplier接口实现提供的对象。
    • T orElseThrow(Supplier<? extends X> exceptionSupplier):如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。

    3.2.3 过滤

    • Optional<T> filter(Predicate<? super <T> predicate):如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional。

    3.2.4 映射

    • Optional map(Function<? super T,? extends U> mapper):如果有值,则对其执行调用映射函数得到返回值。如果返回值不为 null,则创建包含映射返回值的Optional作为map方法返回值,否则返回空Optional。
    • Optional flatMap(Function<? super T, Optional<U>> mapper):如果值存在,就对该值执行提供的mapping函数调用,返回一个Optional类型的值,否则就返回一个空的Optional对象。

    3.3 Optional API 及源码注解

    package java.util;
    
    import java.util.function.Consumer;
    import java.util.function.Function;
    import java.util.function.Predicate;
    import java.util.function.Supplier;
    
    /**
     * 一个可能包含也可能不包含非null值的容器对象。 
     * 如果存在值,则isPresent()返回true 。 
     * 如果不存在任何值,则该对象被视为空,并且isPresent()返回false 。
     * 提供了其他取决于所包含值是否存在的方法,例如orElse()(如果不存在值,则返回默认值)和ifPresent()(如果存在值, 则执行操作)。
     * 这是一个基于值的类; 
     * 在Optional实例上使用标识敏感的操作(包括引用等于(==), 标识哈希码或同步)可能会产生不可预测的结果, 应避免使用
     *
     */
    public final class Optional<T> {
    
        /**
         * empty()通用实例
         */
        private static final Optional<?> EMPTY = new Optional<>();
    
        /**
         * 如果不为空,则为该值;否则为false。 如果为null,则表示不存在任何值
         */
        private final T value;
    
        /**
         * 构造一个空实例。
         *
         * 注意:通常,每个VM仅应存在一个空实例EMPTY 
         */
        private Optional() {
            this.value = null;
        }
    
        /**
         * 返回一个空的Optional实例。 此Optional没有值。
         *
         * 类型参数:<T> –不存在的值的类型
         * 返回值:一个空的Optional
         * api注意:尽管这样做可能很诱人,但应通过将==与Optional.empty()返回的实例进行比较来避免测试对象是否为空。
         * 不能保证它是一个单例。
         * 而是使用isPresent()
         */
        public static<T> Optional<T> empty() {
            @SuppressWarnings("unchecked")
            Optional<T> t = (Optional<T>) EMPTY;
            return t;
        }
    
        /**
         * 使用描述的值构造一个实例。
         *
         * 参数:值–要描述的非null值
         * 抛出:NullPointerException如果值为null
         */
        private Optional(T value) {
            this.value = Objects.requireNonNull(value);
        }
    
        /**
         * 返回一个Optional描述给定的非null值。
         *
         * 参数:value –要描述的值,必须为非null
         * 类型参数:<T> –值的类型
         * 返回值:存在值的Optional
         */
        public static <T> Optional<T> of(T value) {
            return new Optional<>(value);
        }
    
        /**
         * 返回一个描述给定值的Optional ,如果不为null ,则返回一个空的Optional。
         * 
         * 参数:值–描述的可能为null值
         * 类型参数:<T> –值的类型
         * 返回值:一个Optional与如果指定值是非当前值null ,否则一个空Optional
         */
        public static <T> Optional<T> ofNullable(T value) {
            return value == null ? empty() : of(value);
        }
    
        /**
         * 如果存在值,则返回该值,否则抛出NoSuchElementException
         * 返回值:此Optional描述的非null值
         * 抛出:NoSuchElementException如果不存在任何值
         * api注意:此方法的首选替代方法是orElseThrow() 。
         */
        public T get() {
            if (value == null) {
                throw new NoSuchElementException("No value present");
            }
            return value;
        }
    
        /**
         * 如果存在值,则返回true ,否则返回false 。
         *
         * 返回值:如果存在值,则为true ,否则为false
         */
        public boolean isPresent() {
            return value != null;
        }
    
        /**
         * 如果存在值,则使用该值执行给定的操作,否则不执行任何操作。
         *
         * 参数:动作–要执行的动作(如果存在值)
         */
        public void ifPresent(Consumer<? super T> consumer) {
            if (value != null)
                consumer.accept(value);
        }
        
        /**
         * 如果存在值,则使用该值执行给定的操作,否则执行给定的基于空的操作。
         * 参数:动作–要执行的动作(如果存在值)emptyAction –要执行的基于空的操作(如果不存在任何值)
         * 抛出:NullPointerException如果存在一个值并且给定的操作为null ,或者不存在任何值并且给定的基于空的操作为null 
         *  @since 9
         */
        public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
            if (value != null) {
                action.accept(value);
            } else {
                emptyAction.run();
            }
        }
    
        /**
         * 如果存在一个值, 并且该值与给定的谓词匹配,则返回描述该值的Optional,否则返回一个空的Optional
         *
         * 参数:谓词–应用于值的谓词(如果存在)
         * 返回值:一个Optional描述此的值Optional, 如果一个值存在并且该值给定的谓词相匹配, 否则一个空Optional
         * 抛出:NullPointerException如果谓词为null
         */
        public Optional<T> filter(Predicate<? super T> predicate) {
            Objects.requireNonNull(predicate);
            if (!isPresent())
                return this;
            else
                return predicate.test(value) ? this : empty();
        }
    
        /**
         * 如果存在值, 则返回一个Optional描述(就像by ofNullable)将给定映射函数应用于该值的结果, 否则返回一个空的Optional。
         * 如果映射函数返回null结果, 则此方法返回空的Optional 
         */
        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 -bearing映射函数应用于该值的结果,否则返回一个空的Optional
         * 此方法类似于map(Function),但是映射函数是其结果已经是Optional函数,如果调用该函数,则flatMap不会将其包装在其他Optional
         *
         * 参数:mapper –应用于值的映射函数(如果存在)
         * 类型参数:<U> –映射函数返回的Optional值的类型
         * 返回值:施加的结果Optional荷瘤映射函数此的值Optional,如果一个值存在,否则一个空Optional
         * 抛出:NullPointerException如果映射函数为null或返回null结果
         *
         */
        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));
            }
        }
        
        /**
         * 如果值存在时,返回一个Optional描述的值,否则将返回一个Optional产生通过供给功能。
         *
         * 参数:供应商–产生要返回的Optional的供应功能
         * 返回值:返回一个Optional描述此的值Optional ,如果一个值存在,否则Optional所生产的供应功能。
         * 抛出:NullPointerException如果提供的函数为null或产生null结果
         * @since 9
         */
        public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) {
            Objects.requireNonNull(supplier);
            if (isPresent()) {
                return this;
            } else {
                @SuppressWarnings("unchecked")
                Optional<T> r = (Optional<T>) supplier.get();
                return Objects.requireNonNull(r);
            }
        }
    
        /**
         * 如果存在值,则返回仅包含该值的顺序Stream ,否则返回空Stream 。
         *
         * 返回值:作为Stream的可选值
         * @since 9
         */
        public Stream<T> stream() {
            if (!isPresent()) {
                return Stream.empty();
            } else {
                return Stream.of(value);
            }
        }   
    
        /**
         * 如果存在值,则返回该值,否则返回other 。
         *
         * 参数:其他–要返回的值(如果不存在任何值)。 可以为null 。
         * 返回值:值(如果存在),否则other
         */
        public T orElse(T other) {
            return value != null ? value : other;
        }
    
        /**
         * 如果存在值,则返回该值,否则返回由供应函数产生的结果。
         *
         * 参数:供应商–产生要返回的值的供应函数
         * 返回值:值(如果存在),否则提供功能产生的结果
         */
        public T orElseGet(Supplier<? extends T> other) {
            return value != null ? value : other.get();
        }
        
        /**
         * If a value is present, returns the value, otherwise throws
         * {@code NoSuchElementException}.
         *
         * @return the non-{@code null} value described by this {@code Optional}
         * @throws NoSuchElementException if no value is present
         * @since 10
         */
        public T orElseThrow() {
            if (value == null) {
                throw new NoSuchElementException("No value present");
            }
            return value;
        }   
    
        /**
         * Optional中如果存在值,则返回该值,否则抛出由异常提供函数产生(Supplier接口实现)的异常。
         *  
         * 参数:exceptionSupplier –产生要抛出的异常的提供函数
         * 类型参数:<X> –引发的异常类型
         * 返回值:值(如果存在)
         * 抛出:X –如果不存在任何值NullPointerException如果不存在任何值并且异常提供函数为null
         * api注意:带有空参数列表的对异常构造函数的方法引用可用作提供者
         */
        public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
            if (value != null) {
                return value;
            } else {
                throw exceptionSupplier.get();
            }
        }
    }
    

    四、Optional 常用方法及使用示例

    4.1 静态方法 Optional.of()

    • 方法作用: 为指定的值创建一个指定非 null 值的 Optional。
    • 方法描述: of 方法通过工厂方法创建 Optional 实例,需要注意的是传入的参数不能为 null,否则抛出 NullPointerException。
    • 返回类型:Optional
    • 示例代码:
      调用两个 Optional.of() 方法,一个传入正常参数,另一个传入 null 参数:
    public static void main(String[] args) {
        // 传入正常值,正常返回一个 Optional 对象
        Optional<String> optional1 = Optional.of("mydlq");
    
        // 传入参数为 null,抛出 NullPointerException.
        Optional optional2 = Optional.of(null);
    }
    

    运行代码,可以观察到控制台输出内容如下:

    Exception in thread "main" java.lang.NullPointerException
        at java.util.Objects.requireNonNull(Objects.java:203)
        at java.util.Optional.<init>(Optional.java:96)
        at java.util.Optional.of(Optional.java:108)
        at club.mydlq.OptionalExample.main(OptionalExample.java:12)
    

    可以看到传入正常参数正常返回 Optional 对象,传入 null 参数返回 NullPointerException 异常。

    4.2 静态方法 Optional.ofNullable()

    • 方法作用: 为指定的值创建一个 Optional 对象,如果指定的参数为 null,不抛出异常,直接则返回一个空的 Optional 对象。
    • 方法描述: ofNullable 方法是和 of 方式一样,都是用于创建 Optional 对象,只是传入的参数 null 时,会返回一个空的 Optional 对象,而不会抛出 NullPointerException 异常。
    • 返回类型: Optional
    • 示例代码:
      调用 Optional.ofNullable() 方法,传入 null 参数:
    public static void main(String[] args) {
        // 传入正常值,正常返回一个 Optional 对象
        Optional<String> optional1 = Optional.ofNullable("mydlq");
    
        // 传入 null 参数,正常返回 Optional 对象
        Optional optional2 = Optional.ofNullable(null);
    }
    

    运行代码,可以观察到正常传入值和传入 null 值时,都没有抛出异常。

    4.3 对象方法 isPresent()

    • 方法作用: 如果值存在则方法会返回 true,否则返回 false。
    • 方法描述: 该方法其实就是用于判断创建 Optional 时传入参数的值是否为空,实现代码就简单一行,即 value != null 所以如果不为空则返回 true,否则返回 false。
    • 返回类型: boolean
    • 示例代码:
    public static void main(String[] args) {
        // 传入正常值,正常返回一个 Optional 对象,并使用 isPresent 方法
        Optional optional1 = Optional.ofNullable("mydlq");
        System.out.println("传入正常值返回:" + optional1.isPresent());
    
        // 传入参数为 null 生成一个 Optional 对象,并使用 isPresent 方法
        Optional optional2 = Optional.ofNullable(null);
        System.out.println("传入 null 值返回:" + optional2.isPresent());
    }
    

    运行代码,可以观察到控制台输出内容如下:

    传入正常值返回:true
    传入 null 值返回:false
    

    可以看到传入正常参数时调用 Optional 对象的 isPresent 方法时返回 true,传入 null 参数返回 false。

    4.4 对象方法 get()

    • 方法作用: 如果 Optional 有值则将其返回,否则抛出 NoSuchElementException 异常。
    • 方法描述: get 方法内部实现其实就是判断 Otpional 对象中的 value 属性是否为 null,如果是就抛出 NoSuchElementException 异常,否则返回这个 value 值。
    • 返回类型: T
    • 示例代码:
    public static void main(String[] args) {
        // 传入正常值,正常返回一个 Optional 对象,并使用 get 方法获取值
        Optional optional1 = Optional.ofNullable("mydlq");
        System.out.println(optional1.get());
    
        // 传入参数为 null 生成一个 Optional 对象,并使用 get 方法获取值
        Optional optional2 = Optional.ofNullable(null);
        System.out.println(optional2.get());
    }
    

    运行代码,可以观察到控制台输出内容如下:

    传入正常参数:mydlq
    Exception in thread "main" java.util.NoSuchElementException: No value present
        at java.util.Optional.get(Optional.java:135)
        at club.mydlq.OptionalExample.main(OptionalExample.java:14)
    

    BASH可以观察到传入正常值的 Optional 调用 get 方法正常输出值,通过空的 optional 对象使用 get 方法获取值时,抛出 NoSuchElementException 异常。

    4.5 对象方法 ifPresent()

    • 方法作用: 如果值存在则使用该值调用 consumer , 否则不做任何事情。
    • 方法描述: 该方法 ifPresent(Consumer<? super T> consumer) 中参数接收的是 Consumer 类,它包含一个接口方法 accept(),该方法能够对传入的值进行处理,但不会返回结果。这里传入参数可以传入 Lamdda 表达式或 Consumer 对象及实现 Consumer 接口的类的对象。
    • 返回类型: void
    • 示例代码:
    public static void main(String[] args) {
        // 创建 Optional 对象,然后调用 Optional 对象的 ifPresent 方法,传入 Lambda 表达式
        Optional optional1 = Optional.ofNullable("mydlq1");
        optional1.ifPresent((value) -> System.out.println("Optional 的值为:" + value));
    
        // 创建 Optional 对象,调用 Optional 对象的 ifPresent 方法,传入实现 Consumer 匿名内部类
        Optional optional2 = Optional.ofNullable("mydlq2");
        Consumer<String> consumer = new Consumer() {
            @Override
            public void accept(Object value) {
                System.out.println("Optional 的值为:" + value);
            }
        };
        optional2.ifPresent(consumer);
    }
    

    运行代码,可以观察到控制台输出内容如下:

    Optional 的值为:mydlq1
    Optional 的值为:mydlq2
    

    可以观察到,调用 ifPresent 使用 lambda 或者内部匿名类方法,都是为了再执行 Optional 对象的 ifPresent 方法时,执行一段代码逻辑。

    4.6 对象方法 orElse()

    • 方法作用: 如果该值存在就直接返回, 否则返回指定的其它值。
    • 方法描述: orElse 方法实现很简单,就是使用三目表达式对传入的参数值进行 null 验证,即 value != null ? value : other; 如果为 null 则返回 true,否则返回 false。
    • 返回类型: T
    • 示例代码:
    public static void main(String[] args) {
        // 传入正常参数,获取一个 Optional 对象,并使用 orElse 方法设置默认值
        Optional optional1 = Optional.ofNullable("mydlq");
        Object object1 = optional1.orElse("默认值");
        System.out.println("如果值不为空:"+object1);
    
        // 传入 null 参数,获取一个 Optional 对象,并使用 orElse 方法设置默认值
        Optional optional2 = Optional.ofNullable(null);
        Object object2 = optional2.orElse("默认值");
        System.out.println("如果值为空:"+object2);
    }
    

    运行代码,可以观察到控制台输出内容如下:

    如果值不为空:mydlq
    如果值为空:默认值
    

    可以观察到,如果 Optional 的值为空,则返回 orElse() 方法设置的默认值,否则返回 Optional 中的值。

    4.7、对象方法 orElseGet()

    • 方法作用: 如果该值存在就返回值,否则触发 other,并返回 other 调用的结果。
    • 方法描述: orElseGet 方法和 orElse 方法类似,都是在 Optional 值为空时,返回一个默认操作,只不过 orElse 返回的是默认值,而 orElseGet 是执行 lambda 表达式,然后返回 lambda 表达式执行后的结果。
    • 返回类型: T
    • 示例代码:
    public static void main(String[] args) {
        // 传入正常参数,获取一个 Optional 对象,并使用 orElse 方法设置默认值
        Optional optional1 = Optional.ofNullable("mydlq");
        Object object1 = optional1.orElseGet(() -> {
            String defaultVal = "执行逻辑和生成的默认值";
            return defaultVal;
        });
        System.out.println("输出的值为:"+object1);
    
        // 传入 null 参数,获取一个 Optional 对象,并使用 orElse 方法设置默认值
        Optional optional2 = Optional.ofNullable(null);
        Object object2 = optional2.orElseGet(() -> {
            String defaultVal = "执行逻辑和生成的默认值";
            return defaultVal;
        });
        System.out.println("输出的值为:"+object2);
    }
    

    运行代码,可以观察到控制台输出内容如下:

    输出的值为:mydlq
    输出的值为:执行逻辑和生成的默认值
    

    可也观察到,当 Optional 值为不为空时正常返回带值的 Optional,如果 Optional 为空则返回 orElseGet 方法中 lambda 表达式执行后生成的值。

    4.8、对象方法 orElseThrow()

    • 方法作用: 如果 Optional 存在该值,返回包含的值,否则抛出由 Supplier 继承的异常。
    • 方法描述: orElseThrow 方法其实就是判断创建 Optional 时传入的参数是否为 null,如果是非 null 则返回传入的值,否则抛出 异常。
    • 返回类型: T
    • 示例代码:
    public static void main(String[] args) {
        // 传入正常参数,获取一个 Optional 对象,并使用 orElseThrow 方法
        try {
            Optional optional1 = Optional.ofNullable("mydlq");
            Object object1 = optional1.orElseThrow(() -> {
                        System.out.println("执行逻辑,然后抛出异常");
                        return new RuntimeException("抛出异常");
                    }
            );
            System.out.println("输出的值为:" + object1);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    
        // 传入 null 参数,获取一个 Optional 对象,并使用 orElseThrow 方法
        try {
            Optional optional2 = Optional.ofNullable(null);
            Object object2 = optional2.orElseThrow(() -> {
                        System.out.println("执行逻辑,然后抛出异常");
                        return new RuntimeException("抛出异常");
                    }
            );
            System.out.println("输出的值为:" + object2);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
    

    运行代码,可以观察到控制台输出内容如下:

    值为不为空输出的值:mydlq
    执行逻辑,然后抛出异常
    java.lang.RuntimeException: 抛出异常
        at club.mydlq.OptionalExample.lambda$main$1(OptionalExample.java:25)
        at java.util.Optional.orElseThrow(Optional.java:290)
        at club.mydlq.OptionalExample.main(OptionalExample.java:23)
    

    可以观察到,当创建 Optional 时如果传入的参数为空则执行 Lambda 表达式代码逻辑后抛出异常信息,否则返回传入的参数值。

    4.9、对象方法 map()

    • 方法作用: 如果有值,则对其执行调用映射函数得到返回值。如果返回值不为 null,则创建包含映射返回值的 Optional 作为 map 方法返回值,否则返回空 Optional。
    • 方法描述: map 方法主要用于获取某个对象中的某个属性值的 Optional 对象时使用。map 方法调用时,首先验证传入的映射函数是否为空,如果为空则抛出异常。然后,再检测 Optional 的 value 是否为空,如果是,则返回一个空 value 的 Optional 对象。如果传入的映射函数和 Optinal 的 value 都不为空,则返回一个带 value 对象属性的 Optional 对象。
    • 返回类型: Optional
    • 示例代码:

    示例1:创建 Map 集合,存储一些键值对信息,通过 Optional 操作 Map 获取值,然后观察:

    public static void main(String[] args) {
        // 创建 map 对象
        Map<String, String> userMap = new HashMap<>();
        userMap.put("name1", "mydlq");
        userMap.put("name2", null);
    
        // 传入 Map 对象参数,获取一个 Optional 对象,获取 name1 属性
        Optional<String> optional1 = Optional.of(userMap).map(value -> value.get("name1"));
    
        // 传入 Map 对象参数,获取一个 Optional 对象,获取 name2 属性
        Optional<String> optional2 = Optional.of(userMap).map(value -> value.get("name2"));
    
        // 获取 Optional 的值
        System.out.println("获取的 name1 的值:" + optional1.orElse("默认值"));
        System.out.println("获取的 name2 的值:" + optional2.orElse("默认值"));
    }
    

    运行代码,可以观察到控制台输出内容如下:

    获取的 Optional 的值:mydlq
    获取的 Optional 的值:默认值
    

    示例2:创建一个用户类,使用 Optional 操作用户对象,获取其 name 参数,结合 Optional 的 map 方法获取值,进行观察:

    public class User {
    
        private String name;
    
        public User(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }
    

    使用 Optional 的 map 方法对值处理:

    public static void main(String[] args) {
        // 创建一个对象,设置姓名属性而不设置性别,这时候性别为 null
        User user1 = new User("测试名称");
        User user2 = new User();
    
        // 使用 Optional 存储 User 对象
        Optional<User> optional1 = Optional.ofNullable(user1);
        Optional<User> optional2 = Optional.ofNullable(user2);
    
        // 获取对象的 name 属性值
        String name1 = optional1.map(User::getName).orElse("未填写");
        String name2 = optional2.map(User::getName).orElse("未填写");
    
        // 输出结果
        System.out.println("获取的名称:" + name1);
        System.out.println("获取的名称:" + name2);
    }
    

    运行代码,可以观察到控制台输出内容如下:

    获取的名称:测试名称
    获取的名称:未填写
    

    通过上面两个示例观察到,通过 Optional 对象的 map 方法能够获取映射对象中的属性,创建 Optional 对象,并以此属性充当 Optional 的值,结合 orElse 方法,如果获取的属性的值为空,则设置个默认值。

    4.10 对象方法 flatMap()

    • 方法作用: 如果值存在,返回基于 Optional 包含的映射方法的值,否则返回一个空的 Optional。
    • 方法描述: flatMap 方法和 map 方法类似,唯一的不同点就是 map 方法会对返回的值进行 Optional 封装,而 flatMap 不会,它需要手动执行 Optional.of 或 Optional.ofNullable 方法对 Optional 值进行封装。
    • 返回类型: Optional
    • 示例代码:
    public static void main(String[] args) {
        // 创建 map 对象
        Map<String, String> userMap = new HashMap<>();
        userMap.put("name", "mydlq");
        userMap.put("sex", "男");
    
        // 传入 Map 对象参数,获取一个 Optional 对象
        Optional<Map<String, String>> optional1 = Optional.of(userMap);
    
        // 使用 Optional 的 flatMap 方法,获取 Map 中的 name 属性
        // 然后通过获取的值手动创建一个新的 Optional 对象
        Optional optional2 = optional1.flatMap(value -> Optional.ofNullable(value.get("name")));
    
        // 获取 Optional 的 value
        System.out.println("获取的 Optional 的值:" + optional2.get());
    }
    

    运行代码,可以观察到控制台输出内容如下:

    获取的 Optional 的值:mydlq
    

    根据结果观察,可以看到 flatMap 和 map 方法没有什么区别,但是仔细看,代码中调用 flatMap 后,需要手动执行 of 或 ofNullable 方法创建了 Optional 对象。

    4.11 对象方法 filter()

    • 方法作用: 如果有值并且满足断言条件返回包含该值的 Optional,否则返回空 Optional。
    • 方法描述: filter 方法通过传入的限定条件对 Optional 实例的值进行过滤,如果 Optional 值不为空且满足限定条件就返回包含值的 Optional,否则返回空的 Optional。这里设置的限定条件需要使用实现了 Predicate 接口的 lambda 表达式来进行配置。
    • 返回类型: Optional
    • 示例代码:
    public static void main(String[] args) {
        // 创建一个测试的 Optional 对象
        Optional<String> optional = Optional.ofNullable("mydlq");
        // 调用 Optional 的 filter 方法,设置一个满足的条件,然后观察获取的 Optional 对象值是否为空
        Optional optional1 =optional.filter((value) -> value.length() > 2);
        System.out.println("Optional 的值不为空::" + optional.isPresent());
    
        // 调用 Optional 的 filter 方法,设置一个不满足的条件,然后观察获取的 Optional 对象值是否为空
        Optional optional2 =optional.filter((value) -> value.length() <2);
        System.out.println("Optional 的值不为空::" + optional2.isPresent());
    }
    

    运行代码,可以观察到控制台输出内容如下:

    Optional 的值不为空:true
    Optional 的值不为空:false
    

    根据结果可以观察到,可以通过 filter 设置一个条件来判断 Optional 的值,如果满足条件就返回带值的 Optional,否则返回空的 Optional。

    4.12 Optional 常用示例组合

    Optional 类(java.util.Optional) 是一个容器类,它可用保存类型的 T 的值,即使 T 为 null 也可以使用 Optional 存储,这样我就不用显示进行空值检测,防止空指针异常。

    上面也介绍了 Optional 的各种方法,在实际使用中这些方法常常组合使用。且很多方法也常与 Lambda 表达式结合,获取我们想要的结果的值。

    下面是常用的示例,可以作为参考:

    对集合中的对象属性进行过滤

    创建一个 User 对象实体类,里面包含 name 属性:

    public class User {
        private String name;
    
        public User(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }
    

    创建一个使用 main 方法的类,创建几个 User 对象且设置不同的值,有的对象为 null 有的属性不设置,然后通过 Optional 获取 name 属性值加入集合,进行测试:

    import java.util.ArrayList;
    import java.util.List;
    import java.util.Optional;
    
    public class OptionalExample {
    
        /**
         * 测试的 main 方法
         */
        public static void main(String[] args) {
            // 创建一个测试的用户集合
            List<User> userList = new ArrayList<>();
    
            // 创建几个测试用户
            User user1 = new User("abc");
            User user2 = new User("efg");
            User user3 = null;
    
            // 将用户加入集合
            userList.add(user1);
            userList.add(user2);
            userList.add(user3);
    
            // 创建用于存储姓名的集合
            List<String> nameList = new ArrayList();
            // 循环用户列表获取用户信息,值获取不为空且用户以 a 开头的姓名,
            // 如果不符合条件就设置默认值,最后将符合条件的用户姓名加入姓名集合
            for (User user : userList) {
                nameList.add(Optional.ofNullable(user).map(User::getName).filter(value -> value.startsWith("a")).orElse("未填写"));
            }
    
            // 输出名字集合中的值
            System.out.println("通过 Optional 过滤的集合输出:");
            nameList.stream().forEach(System.out::println);
        }
    
    }
    

    输出运行结果:

    通过 Optional 过滤的集合输出:
    abc
    未填写
    未填写
    

    通过上面,可以观察到,使用 Optional 有时候可以很方便的过滤一些属性,而且它的方法可以通过链式调用,方法间相互组合使用,使我们用少量的代码就能完成复杂的逻辑。

    map经典判空之嵌套if

    判断map不为空,map中的key-value不为null且不为空白字符串,常规玩法要么是一组嵌套if,要是是一个tay-catch。

    public static void main(String[] args) {
        System.out.println(test1());
        System.out.println(test2());
        System.out.println(test3());
        System.out.println(test4());
    }
    
    
    public static String test1() {
        //假装外部传入不可控map一个,比如sevlet接受前端传参
        Map pm = new HashMap();
        pm.put("key1", "value1");
    
    
        Optional<Map> mapOptional = Optional.ofNullable(pm);
    
        return mapOptional.map(map -> map.get("key1"))
                .filter(value -> !"".equals(value))
                .flatMap(value -> {
                    //一大堆业务处理...
                    return Optional.ofNullable("test1成功执行");
                }).orElse("test1停车了");
    }
    
    
    public static String test2() {
        //假装外部传入不可控map一个,比如sevlet接受前端传参
        Map pm = new HashMap();
        pm.put("key1", "");
        Optional<Map> mapOptional = Optional.ofNullable(pm);
        return mapOptional.map(map -> map.get("key1"))
                .filter(value -> !"".equals(value))
                .flatMap(value -> {
                    //一大堆业务处理...
                    return Optional.ofNullable("test2成功执行");
                }).orElse("test2停车了");
    }
    
    
    public static String test3() {
        //假装外部传入不可控map一个,比如sevlet接受前端传参
        Map pm = new HashMap();
        pm.put("key1", null);
        Optional<Map> mapOptional = Optional.ofNullable(pm);
        return mapOptional.map(map -> map.get("key1"))
                .filter(value -> !"".equals(value))
                .flatMap(value -> {
                    //一大堆业务处理...
                    return Optional.ofNullable("test3成功执行");
                }).orElse("test3停车了");
    }
    
    
    public static String test4() {
        //假装外部传入map一个,不传指定key
        Map pm = new HashMap();
    
        Optional<Map> mapOptional = Optional.ofNullable(pm);
        return mapOptional.map(map -> map.get("key1"))
                .filter(value -> !"".equals(value))
                .flatMap(value -> {
                    //开始啪啪业务逻辑 发短信啊crud啊...
                    return Optional.ofNullable("test4成功执行");
                }).orElse("test4停车了");
    }
    

    输出运行结果:

    test1成功执行
    test2停车了
    test3停车了
    test4停车了
    

    总结一下:

    五、Java 9 增强

    上面介绍了 Java 8 的特性,Java 9 为 Optional 类添加了三个方法:or()、ifPresentOrElse() 和 stream()。

    5.1 or() 方法

    or()函数源码如下:

    /**
     * 如果值存在时,返回一个Optional描述的值,否则将返回一个Optional产生通过供给功能。
     *
     * 参数:供应商–产生要返回的Optional的供应功能
     * 返回值:返回一个Optional描述此的值Optional ,如果一个值存在,否则Optional所生产的供应功能。
     * 抛出:NullPointerException如果提供的函数为null或产生null结果
     * @since 9
     */
    public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) {
        Objects.requireNonNull(supplier);
        if (isPresent()) {
            return this;
        } else {
            @SuppressWarnings("unchecked")
            Optional<T> r = (Optional<T>) supplier.get();
            return Objects.requireNonNull(r);
        }
    }
    

    or() 方法与 orElse() 和 orElseGet() 类似,它们都在对象为空的时候提供了替代情况。or() 的返回值是由 Supplier 参数产生的另一个 Optional 对象。

    如果对象包含值,则 Lambda 表达式不会执行:

    public void whenEmptyOptional_thenGetValueFromOr() {
        User result = Optional.ofNullable(user)
          .or( () -> Optional.of(new User("default","1234"))).get();
    
        assertEquals(result.getEmail(), "default");
    }
    

    上面的示例中,如果 user 变量是 null,它会返回一个 Optional,它所包含的 User 对象,其电子邮件为 “default”。

    5.2 ifPresentOrElse() 方法

    ifPresentOrElse() 方法需要两个参数:一个 Consumer 和一个 Runnable。如果对象包含值,会执行 Consumer 的动作,否则运行 Runnable。源码如下:

    /**
     * 如果存在值,则使用该值执行给定的操作,否则执行给定的基于空的操作。
     * 参数:动作–要执行的动作(如果存在值)emptyAction –要执行的基于空的操作(如果不存在任何值)
     * 抛出:NullPointerException如果存在一个值并且给定的操作为null ,或者不存在任何值并且给定的基于空的操作为null 
     *  @since 9
     */
    public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
        if (value != null) {
            action.accept(value);
        } else {
            emptyAction.run();
        }
    }
    

    如果你想在有值的时候执行某个动作,或者只是跟踪是否定义了某个值,那么这个方法非常有用:

    Optional.ofNullable(user).ifPresentOrElse( u -> logger.info("User is:" + u.getEmail()),() -> logger.info("User not found"));
    

    5.3 stream()

    最后介绍的是新的 stream() 方法,它通过把实例转换为 Stream 对象,让你从广大的 Stream API 中受益。如果没有值,它会得到空的 Stream;有值的情况下,Stream 则会包含单一值。源码如下:

    /**
     * 如果存在值,则返回仅包含该值的顺序Stream ,否则返回空Stream 。
     *
     * 返回值:作为Stream的可选值
     * @since 9
     */
    public Stream<T> stream() {
        if (!isPresent()) {
            return Stream.empty();
        } else {
            return Stream.of(value);
        }
    }   
    

    我们来看一个把 Optional 处理成 Stream 的例子:

    public void whenGetStream_thenOk() {
        User user = new User("john@gmail.com", "1234");
        List<String> emails = Optional.ofNullable(user)
          .stream()
          .filter(u -> u.getEmail() != null && u.getEmail().contains("@"))
          .map( u -> u.getEmail())
          .collect(Collectors.toList());
    
        assertTrue(emails.size() == 1);
        assertEquals(emails.get(0), user.getEmail());
    }
    

    这里对 Stream 的使用带来了其 filter()、map() 和 collect() 接口,以获取 List。

    Optional做为空判断时,注意orElse和orElseGet使用区别

    • 使用 Optional.ofNullable(obj).orElse 时,无论 obj 是否为 null,都会执行 orElse 的方法。
    • 使用 Optional.ofNullable(obj).orElseGet 时,只有 obj 是否为 null,才会执行 orElseGet 里的方法。
    • 传入Optonal值为非空,orElse或orElseGet都会执行,返回传入值。
    • 传入Optional值为空,orElse会执行,orElseGet不会执行,返回执行方法体的结果。
    • 传入Optonal值非空,返回本身。
    • 传入Optional值为空,返回other。
    • 由于orElseGet入参使用的是Supplier(类似懒加载),所以在只有在传入Optional值为空的时候,才会去执行。
    • 对于orElseGet:返回值(如果存在),否则调用{other}并返回该调用的结果。
    • 对于orElse来说,无论传入Optional值是否为空,都会执行。
    • 对于orElse:返回值(如果存在),否则返回{other}。
    • 对于性能要求比较高的场景,建议使用orElseGet。

    参考:
    https://www.aqwdzy.com/content/84

    https://blog.csdn.net/weixin_45821811/article/details/115656637

    https://blog.csdn.net/weixin_43888891/article/details/124788806

    https://www.cnblogs.com/jing99/p/13715819.html

    https://blog.csdn.net/murongyuyao/article/details/123891391

    相关文章

      网友评论

        本文标题:Optional 使用详解

        本文链接:https://www.haomeiwen.com/subject/tnymwrtx.html