欢迎您访问新疆栾骏商贸有限公司,公司主营电子五金轴承产品批发业务!
全国咨询热线: 400-8878-609

新闻资讯

常见问题

Java8中Optional操作的实际应用

作者:用户投稿2026-01-11 06:55:46
目录
  • 简介
  • 正文
    • 1. Optional是什么
    • 2. 没它 VS 有它
    • 3. 核心操作
  • 4. 应用
    • 总结
      • 总结

        简介

        目的:Optional的出现主要是为了解决null指针问题,也叫NPE(NullPointerException)

        外形:Optional外形酷似容器(其实它就是一个容器),只是这个容器比较特殊,因为它只能存放一个对象,运气不好的话这个对象还是个null

        操作:Optional从操作上来看,又跟前面的Stream流式操作很像,比如过滤filter - 提取map等

        下面我们用比较简单的例子来对比着看下,Optional的一些基础用法

        正文

        1. Optional是什么

        Optional是一个容器,只能存放一个对象(可为null)

        Optional的出现是

        • 一个是为了解决NPE问题(阿里开发手册也有提到这一点,点击可直接下载,官方链接)
        • 另一个是为了代码更加清晰可读,因为Optional这个名字的灵感就是来自英文optional(可选的),意思就是说这个对象可以为空,可以不为空

        2. 没它 VS 有它

        下面我们用旧代码和新代码来对比着看(所谓的新旧是以Java8为分割线)

        案例1:现有C类,我们要提取C.name属性

        public class OptionalDemo {
            private static final String DEFAULT_NAME = "javalover";
        
            public static void main(String[] args) {
              // 传入null,以身试法  
              getName(null);
            }
            // 取出c.name
            public static void getName(C c){
                // 旧代码 Java8之前
                String name = (c!=null ? c.getName() : DEFAULT_NAME);
                System.out.println("old: "+name);
                // 新代码 Java8之后(下面的三个操作方法后面会介绍,这里简单了解下)
                String nameNew = Optional
                    				// 工厂方法,创建Optional<C>对象,如果c为null,则创建空的Optional<C>对象
                                    .ofNullable(c)
                    				// 提取name,这里要注意,即使c==null,这里也不会抛出NPE,而是返回空的Optional<String>,所以在处理数据时,我们不需要担心空指针异常
                                    .map(c1->c1.getName())
                    				// 获取optional的属性值,如果为null,则返回给定的实参DEFAULT_NAME
                                    .orElse(DEFAULT_NAME);
        
                System.out.println("new: "+nameNew);
            }
        }
        class C{
            private String name;
        
            public C(String name) {
                this.name = name;
            }
          // 省略getter/setter
        }

        乍一看,好像Java8之前的旧代码更合适啊,只需要一个三目运算符

        再看Optional操作,发现并没有那么简洁

        是这样的,如果只是一层判断,那普通的if判断做起来更方便;

        但是如果嵌套两层呢,比如b.getC().getName()?

        下面我们就看下,两层嵌套会怎么样

        例子2:现多了一个B类(依赖C类),我们要从对象B中提取C的属性name,即b.getC().getName()

        public static void getName2(B b){
                // 旧代码
                String name = (b!=null ? ( b.getC()!=null ? b.getC().getName() : DEFAULT_NAME) : DEFAULT_NAME);
                // 新代码
                String nameNew = Optional
                        .ofNullable(b)
                        .map(b1->b1.getC())
                        .map(c1->c1.getName())
                        .orElse(DEFAULT_NAME);
                System.out.println(nameNew);
            }
        
        class B{
            private C c;
        
            public B(C c) {
                this.c = c;
            }
          // 省略getter/setter
        }

        这次不管是乍一看,还是一直看,都是Optional更胜一筹

        例子3:现多了一个A类(依赖B类),我们要提取a.getB().getC().getName()

        等等等,省略号

        意思到就行,反正要说的就是单从判空来看的话,Optional肯定是好过三目运算符的(if/else这里就不举了,它的嵌套只会更多)

        3. 核心操作

        因为Optional主要是操作数据(类似数据库操作),所以我们这里从数据的角度来进行分析

        这里我们可以分为三种操作:保存数据、处理数据、获取数据

        保存数据

        • (没有默认值)public static <T> Optional<T> of(T value) :填充 T value 到 Optional 的属性中;如果 value==null,则抛出NPE
        • (默认null)public static <T> Optional<T> ofNullable(T value) :填充 T value 到 Optional 的属性中;如果引用为null,则填充null
        • (构造一个空的Optional)public static<T> Optional<T> empty():单纯地创建一个数据为null的空Optional,即直接填充null到 Optional 的属性中【不常用】

        处理数据

        • (提取)public<U> Optional<U> map(Function<? super T, ? extends U> mapper) :提取Optional中属性T的某个属性值U,并将U填充到新的Optional中并返回
        • (过滤)public Optional<T> filter(Predicate<? super T> predicate) :过滤Optional中属性T的某个属性值,符合条件则将T填充到新的Optional中并返回
        • (扁平化提取)public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper):提取Optional中属性T的某个属性Optional<U>,直接返回

        获取数据

        • public T orElse(T other) :获取数据,如果数据为null,则返回T other
        • public T orElseGet(Supplier<? extends T> other):获取数据,如果数据为null,则通过函数式接口other返回一个新的数据T
        • public T get() :获取数据,如果数据为null,则报NPE【不常用】

        上面这些操作中,不常用的就是get()和empty()

        其他的就不举了,这里主要说下map()和flatMap()

        如下图所示:

        map()主要是提取Optional中的属性C的属性name,然后再包装到新的Optional

        输入Optional<C>, 输出Optional<String>(即Optional<c.name>)

        String nameNew = Optional
                            .ofNullable(c)
                            .map(c1->c1.getName())
                            .orElse("xxx");

        flatMap()主要是提取Optional中的属性B的Optional<C>属性中的C的值,然后再包装到新的Optional

        输入Optional<B>,输出Optional<C>

        public class FlatMapDemo {
            private static final String DEFAULT_NAME = "javalover";
        
            public static void main(String[] args) {
                getName(null);
            }
        
            // 取出 b.c.name
            public static void getName(B b){
                C c = Optional
                        .ofNullable(b)
                    	// 这里扁平化处理,提取Optional<C>中的C
                    	// 如果用map,则返回的是Optional<Optional<C>>
                        .flatMap(b->b.getC())
                        .orElse(new C("xxx"));
                System.out.println(c.getName());
            }
        }
        
        class B{
            private Optional<C> c;
        
            public Optional<C> getC() {
                return c;
            }
        
            public void setC(C c) {
                this.c = Optional.ofNullable(c);
            }
        }
        
        class C{
            private String name;
        
            public C(String name) {
                this.name = name;
            }
            // 省略getter/setter
        }

        4. 应用

        从规范角度来讲,是为了代码清晰,一看用Optional<T>变量,就知道T可能为null;

        从编码角度来讲,主要是应用在非空判断;但是实际场景的话,有两个

        • 没有用Optional进行包裹的参数:比如上面讲到的例子,传来的参数就是普通对象,我们就需要自己用Optional容器来包裹传来的参数,然后进行后续操作
        // 取出c.name
        public static void getName(C c){
            // 自己手动包装 Optional<C>
            String nameNew = Optional
                .ofNullable(c)
                .map(c1->c1.getName())
                .orElse(DEFAULT_NAME);
        
            System.out.println("new: "+nameNew);
        }
        • 有用Optional进行包裹的参数:比如数据库查询时,我们可以用Optional来包裹查询的结果并返回,这样我们分析结果的时候,只需要通过orElse()来获取,同时还可以设定默认值
        // 返回Optional<Car>,通过.orElse(defaultCar)就可以获取返回值,如果返回值为null,还可以设定一个默认值defaultCar
        Optional<Car> selectOne(SelectStatementProvider selectStatement);

        总结

        • Optional是什么:一个容器,存放一个对象,对象可以为null

        • 没它 VS 有它:看场景

          • 如果只是单个的if/else判断,那就没它会好点;

          • 如果嵌套比较多,或者本来传来的数据就是Optional类型,那肯定是Optional合适

        • 核心操作:不常用的这里就不写了

          • 保存数据:工厂方法 of()ofNullable()
          • 处理数据:map(), filter(), flatMap()
          • 获取数据:orElse()
        • 应用:主要用在非空判断,实际场景的话,我们可以用在数据库查询语句中

        总结