Java 8的Optional的正确打开方式

2020-02-02  本文已影响0人  祗談風月

本文在理清Optional的基本用法的同时,附带对比Optional中的map/flatMap和stream中的map/flatMap。

问题

Java 8中的Optional号称可以避免NPE(Null PointerException) 。但在日常工作中看到很多代码中Optional的使用,是用 isPresent() 函数来判别是否为null。下面这样的判断方式,还不如直接 bean !=null 这样的判断,区别只是没有出现null这个关键词而已。更不用说链式的,不停的调用isPresent()。
如下:

if(beanOpt.isPresent()){
  do somethings..
}

if(beanOpt.isPresent() && beanOpt.get().getBean1().isPresent()){
  beanOpt.get().getBean1().get() ... do somethings
}

这样的代码让阅读的人不想读,修改的人不愿意修改。

这是起到了规避Null判断的作用,但是多此一举,显然,也不是Optional的正确用法。避免NPE是要Optional配合map / flatmap函数使用的。

map / flatMap的基本含义

map/flatMap在函数式编程中非常常用(map与reduce组成经典的map-reduce编程范式,flatMap是map的一种语法糖)。通常map是做元素映射,flatMap是做映射的同时去掉多层级的集合,只保留一层(所谓flat——打平)。Optional也延用了这两个名称,基本含义保持,特别的是Optional中仅一个元素,打平是去optional的包装,只保留一层。

Optional的map, flatmap的用法

  1. map/flatMap的函数参数中,函数的输入是optional内被包装的对象,因此不需要显式处理optional的拆箱操作(用拆箱表达从optional中get元素的动作)
  2. map/flatMap的返回值会被自动包装到optional内返回,可以使用orElse(),ifPresent()来避免if-else的null判断
  3. 因为map/flatMap的这个自动拆optional的这个特性,可以对于多层optional嵌套可以链式map搞定
  4. 这个自动拆箱的过程不会抛出NPE,为null的元素会作为Optional.empty()向后传递
  5. map/flatMap的区别在于,如果函数参数中的返回值为optional,flatMap只会留一层option包装,而map会直接保留原有optional的包装(见第二段代码示例),因此flatMap的函数参数中的返回值必须是optional包装对象(这个限制在函数声明中已经限制)

这些就是optional为处理NPE的工作。除此外optional还有很多方法,都非常简洁表意,不再赘述。

// map 处理NPE
    public static void main(String[] args) {
        Optional<String> strOpt = Optional.of("hello world!");
        Optional<String> emptyOpt = Optional.empty();
        
        System.out.println(getStrLength(strOpt));
        System.out.println(getStrLength(emptyOpt));
    }

    private static Integer getStrLength(Optional<String> strOpt) {
        return strOpt.map(String::length).orElse(0);
    }

--输出------
12
0

Process finished with exit code 0
// map / flatMap 的对比

    public static void main(String[] args) {
        Optional<Bean> beanOpt = Optional.of(new Bean("beanId"));
        Optional<Bean> emptyOpt = Optional.empty();

        System.out.println(testFlatMap(beanOpt));
        System.out.println(testFlatMap(emptyOpt));

        System.out.println(testMap(beanOpt));
        System.out.println(testMap(emptyOpt));
    }

    private static String testFlatMap(Optional<Bean> strOpt) {
        return strOpt.flatMap(Bean::getId).orElse("noneId");
    }


    private static Optional<String> testMap(Optional<Bean> strOpt) {
        return strOpt.map(Bean::getId).orElse(Optional.of("noneId"));
    }

    static class Bean{
        String id;

        public Bean(String id) {
            this.id = id;
        }

        public Optional<String> getId() {
            return Optional.ofNullable(id);
        }

        public void setId(String id) {
            this.id = id;
        }
    }

---输出--------
beanId
noneId
Optional[beanId]
Optional[noneId]

Process finished with exit code 0

延展:函数式中的map, flatmap

    public static void main(String[] args) {
        List<Bean> beans = new ArrayList<>();
        beans.add(new Bean("bean1"));
        beans.add(new Bean("bean2"));
        beans.add(new Bean());

        // 对每个beans中的bean,获取他们的id字段,转换为对应的集合
        
        List<String> ids = beans.stream()
                .map(bean -> bean.getId())
                .map(opt -> opt.orElse("noneId"))
                .collect(Collectors.toList());

        System.out.println(ids.stream().collect(Collectors.joining(",")));
    }

    static class Bean{
        String id;

        public Bean(String id) {
            this.id = id;
        }
        public Bean() {
        }

        public Optional<String> getId() {
            return Optional.ofNullable(id);
        }

        public void setId(String id) {
            this.id = id;
        }
    }
---- 输出-------------------
bean1,bean2,noneId

Process finished with exit code 0



    public static void main(String[] args) {
        Stream<Bean> beanStream = Stream.of(
                new Bean("bean1"), 
                new Bean("bean2"), 
                new Bean());

        // 对每个beans中的bean,获取他们的id字段,转换为对应的集合
        List<String> chars =  beanStream
                .map(bean -> bean.getId().orElse("noneId"))
                .flatMap(id -> Arrays.stream(id.split("")))
                .collect(Collectors.toList());
        System.out.println(chars.stream().collect(Collectors.joining(",")));
    }

    static class Bean{
        String id;

        public Bean(String id) {
            this.id = id;
        }
        public Bean() {
        }

        public Optional<String> getId() {
            return Optional.ofNullable(id);
        }

        public void setId(String id) {
            this.id = id;
        }
    }

-----输出----------------
b,e,a,n,1,b,e,a,n,2,n,o,n,e,I,d

Process finished with exit code 0
上一篇下一篇

猜你喜欢

热点阅读