美文网首页
Java 8的Optional的正确打开方式

Java 8的Optional的正确打开方式

作者: 祗談風月 | 来源:发表于2020-02-02 11:24 被阅读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的用法

    • 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

    • map的基本用法
        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
    
    
    • flatMap的基本用法
    
    
        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
    

    相关文章

      网友评论

          本文标题:Java 8的Optional的正确打开方式

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