• Java8-Optional工具类(有效防止空指针异常)


    前言

    • 到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见的原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。
    • Optional 类(java.util.Optional)是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常。
    • Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在,则isPresent()方法会返回true,调用get()方法会返回该对象。

    1、方法使用

    Optional提供很多有用的方法,这样我们就不用显式进行空值检测

    • 创建Optional类对象的方法
      • Optional.of(T t):创建一个Optional实例,t必须非空
      • Optional.empty():创建一个空的Optional实例
      • Optional.ofNullable(T t):创建一个Optional实例,t可以为null
    • 判断Optional容器中是否包含对象
      • boolean isPresent():判断是否包含对象
      • void ifPresent(Consumer consumer):如果有值,就执行Consumer接口的实现方法,并且该值会作为参数传给它
    • 获取Optional容器的对象
      • T get():如果调用对象包含值,返回该值,否则抛异常
      • T orElse(T other):如果有值则将其返回,否则返回指定的other对象
      • T orElseGet(Supplier other):如果有值则将其返回,否则返回由Supplier接口实现提供的对象
      • T orElseThrow(Supplier exceptionSupplier):如果有值则将其返回,否则抛出由Supplier接口实现提供的异常
    • 对容器中的对象进行操作,以下三个方法和下一篇Stream流的方法使用基本一样
      • Optional filter(Predicate predicate):如果该值存在并且满足提供的谓词,就返回包含该值的Optional对象;否则返回一个空的Optional对象
      • Optional map(Function mapper):如果值存在,就对该值执行提供的mapping函数调用
      • Optional flatMap(Function> mapper):如果值存在,就对执行提供的mapping函数调用,返回一个Optional类型的值,否则就返回一个空的Optional对象

    Optional.of(T t)

    public static void main(String[] args){
        String s = new String("123");
        Optional<String> s1 = Optional.of(s);
        System.out.println(s1); // Optional[123]
    
        s = null;
        Optional<String> s2 = Optional.of(s);
        System.out.println(s2); // java.lang.NullPointerException
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Optional.empty()

    public static void main(String[] args){
        Optional<Object> empty = Optional.empty();
        System.out.println(empty); // Optional.empty
    }
    
    • 1
    • 2
    • 3
    • 4

    Optional.ofNullable(T t)

    public static void main(String[] args){
        String s1 = new String("123");
        Optional<String> o1 = Optional.ofNullable(s1);
        System.out.println(o1); // Optional[123]
    
        s1 = null;
        Optional<String> o2 = Optional.ofNullable(s1);
        System.out.println(o2); // Optional.empty
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    boolean isPresent()

    public static void main(String[] args){
        String s1 = new String("123");
        Optional<String> o1 = Optional.ofNullable(s1);
        boolean p1 = o1.isPresent();
        System.out.println(p1); // true
    
        s1 = null;
        Optional<String> o2 = Optional.ofNullable(s1);
        boolean p2 = o2.isPresent();
        System.out.println(p2); // false
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    void ifPresent(Consumer consumer)

    public static void main(String[] args){
        String s1 = new String("123");
        Optional<String> o1 = Optional.ofNullable(s1);
        o1.ifPresent(System.out::println); // 123
    
        s1 = null;
        Optional<String> o2 = Optional.ofNullable(s1);
        o2.ifPresent(System.out::println); // ...null值,不执行里面函数,无结果
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    T get()

    public static void main(String[] args){
        String s1 = new String("123");
        Optional<String> o1 = Optional.ofNullable(s1);
        String g1 = o1.get();
        System.out.println(g1); // 123
    
        s1 = null;
        Optional<String> o2 = Optional.ofNullable(s1);
        String g2 = o2.get();
        System.out.println(g2); // java.util.NoSuchElementException: No value present
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    T orElse(T other)

    public static void main(String[] args){
        String s1 = new String("123");
        Optional<String> o1 = Optional.ofNullable(s1);
        // o1包含对象,不使用234
        String or1 = o1.orElse("234");
        System.out.println(or1); // 123
    
        s1 = null;
        Optional<String> o2 = Optional.ofNullable(s1);
        // o2不包含对象,使用345
        String or2 = o2.orElse("345");
        System.out.println(or2); // 345
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    T orElseGet(Supplier other)

    public static void main(String[] args){
        String s1 = new String("123");
        Optional<String> o1 = Optional.ofNullable(s1);
        // o1包含对象,不执行里面函数式接口,返回123
        String or1 = o1.orElseGet(() -> "234");
        System.out.println(or1); // 123
    
        s1 = null;
        Optional<String> o2 = Optional.ofNullable(s1);
        // o2不包含对象,执行里面函数式接口,返回345
        String or2 = o2.orElseGet(() -> "345");
        System.out.println(or2); // 345
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    T orElseThrow(Supplier exceptionSupplier)

    public static void main(String[] args){
        String s1 = new String("123");
        Optional<String> o1 = Optional.ofNullable(s1);
        String or1 = o1.orElseThrow(() -> new RuntimeException("123"));
        System.out.println(or1); // 123
    
        s1 = null;
        Optional<String> o2 = Optional.ofNullable(s1);
        // 本行直接异常:Exception in thread "main" java.lang.RuntimeException: 234
        o2.orElseThrow(() -> new RuntimeException("234"));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Optional filter(Predicate predicate)

    public static void main(String[] args){
        String s1 = new String("123");
        Optional<String> o1 = Optional.ofNullable(s1);
        // 如果满足条件,就返回包含该值的Optional对象
        Optional<String> f1 = o1.filter(opt -> "123".equals(opt));
        // 如果不满足条件,就返回一个空的Optional对象
        Optional<String> f2 = o1.filter(opt -> "234".equals(opt));
        System.out.println(f1); // Optional[123]
        System.out.println(f2); // Optional.empty
    
        s1 = null;
        Optional<String> o2 = Optional.ofNullable(s1);
        // 如果不包含对象或不满足条件,就返回一个空的Optional对象
        Optional<String> f3 = o2.filter(opt -> "123".equals(opt));
        System.out.println(f3); // Optional.empty
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    Optional map(Function mapper)

    public static void main(String[] args){
        String s1 = new String("123");
        Optional<String> o1 = Optional.ofNullable(s1);
        // 如果值存在,就对该值执行提供的mapping函数调用
        Optional<Integer> m1 = o1.map(opt -> opt.length());
        System.out.println(m1); // Optional[3]
    
        s1 = null;
        Optional<String> o2 = Optional.ofNullable(s1);
        // 如果值不存在,就返回一个空的Optional对象
        Optional<Integer> m2 = o2.map(opt -> opt.length());
        System.out.println(m2); // Optional.empty
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Optional flatMap(Function> mapper)

    public static void main(String[] args){
        String s1 = new String("123");
        Optional<String> o1 = Optional.ofNullable(s1);
        // map方法,可能会形成嵌套关系
        Optional<Optional<String>> m1 = o1.map(opt -> Optional.ofNullable(opt));
        // 使用flatMap方法,会自动拆解嵌套关系
        Optional<String> s = o1.flatMap(opt -> Optional.ofNullable(opt));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2、举例使用

    2.1、举例一:

    通过Teacher获取其下Student的姓名

    public static void main(String[] args){
        Teacher t = new Teacher();
        // 以下代码很容易空指针异常,有可能t为null,有可能t.getStu为null,所以不安全
        // t.getStu().getName();
    
        // 此时的Teacher一定不为null
        Teacher teacher = Optional.ofNullable(t).orElse(new Teacher());
        Student stu = teacher.getStu();
    
        // 此时的Student一定不为null
        Student student = Optional.ofNullable(stu).orElse(new Student());
        String name = student.getName();
        System.out.println(name);
    }
    
    class Teacher{
        private Student stu;
    
        public Student getStu() {
            return stu;
        }
    
        public void setStu(Student stu) {
            this.stu = stu;
        }
    }
    
    class Student{
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    2.2、举例二

    假设你现在有一个HashMap类型的数据,你需要从这个map中读取一个值,该值是以秒为单位计量的一段时间,由于时间必须是整数。如果给定属性对应的值是一个代表正整数的字符串,就返回该整数值,任何其他的情况都返回0

    (1)、jdk1.8以前写法
    public class Test{
        public static void main(String[] args){
            // 假设你只有这个map,只知道key,不知道里面的value
            HashMap<String, Object> map = new HashMap<>();
            map.put("aaa","3");
            map.put("bbb","true");
            map.put("bbb","-1");
            System.out.println(readMap(map, "aaa")); // 3
            System.out.println(readMap(map, "bbb")); // 0
            System.out.println(readMap(map, "ccc")); // 0
        }
    
        // jdk1.8之前写法   安全,但麻烦
        public static int readMap(Map map, String key){
            if(map != null){
                // 知道值肯定为String类型,
                String s = (String) map.get(key);
    
                // 如果值为null或不是一个可以转为Integer类型的字符串,会异常,这里catch住
                try {
                    int i = Integer.parseInt(s);
                    if(i > 0){
                        return i;
                    }
                }catch (NumberFormatException e){
                }
            }
            // 如果没有返回i,其他情况就返回0
            return 0;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    (2)、jdk1.8写法
    public class Test{
        public static void main(String[] args){
            // 假设你只有这个map,只知道key,不知道里面的value
            HashMap<String, Object> map = new HashMap<>();
            map.put("aaa","3");
            map.put("bbb","true");
            map.put("bbb","-1");
            System.out.println(readMap(map, "aaa")); // 3
            System.out.println(readMap(map, "bbb")); // 0
            System.out.println(readMap(map, "ccc")); // 0
        }
    
        // jdk1.8之前写法   安全,但麻烦
        public static int readMap(Map map, String key){
            if(map != null){
                // 先把获取到的值放到Optional类里面
                return Optional.ofNullable((String)map.get(key))
                        // 然后通过flatMap方法,把获取到的返回值拆解出来
                        // 此处使用map将会返回Optional>
                        // 使用flatMap会返回Optional,方便后续处理数据
                        .flatMap(Test::stringToInteger)
                        // 过滤,获取到的值,如果满足条件,则返回i,不走orElse
                        .filter(i -> i>0)
                        // 如果filter内方法执行为false,则返回Optional.empty,则返回orElse里面的内容
                        .orElse(0);
            }
            return 0;
        }
    
        /*
            可以将多个类似的方法封装到一个工具类中,称之为OptionalUtil
            通过这种方式,以后就能直接调用OptionalUtil.stringToInteger方法
            将String转换为Optional对象。
         */
        public static Optional<Integer> stringToInteger(String s){
            try {
                return Optional.of(Integer.parseInt(s));
            }catch (NumberFormatException e){
                return Optional.empty();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    3、总结

    • null引用在历史上被引入到程序设计语言中,目的是为了表示变量值的缺失
    • Java 8中引入了一个新的类java.util.Optional,对存在或缺失的变量值进行建模
    • 你可以使用静态工厂方法Optional.empty、Optional.of以及Optional.ofNullable创建Optional对象
    • Optionial类支持多种方法、如map、flatMap、filter它们在概念上与Stream类中对应的方法十分相似
    • 使用Optional会迫使你更积极地了解引用Optional对象,以应对变量缺失的问题,最终,你能更有效地防止代码中出现不期而至的空指针异常。
    • 使用Optional能帮助你设计更好的API,用户只需要阅读方法签名,就能了解方法是否接收一个Optional类型的值
  • 相关阅读:
    分布式事务解决方案汇总
    设计模式之单例模式(2)
    HIMA Z7系列卡件Z7306、Z7138、Z7127、Z7126、Z7116
    Linux常用操作命令大全
    使用凌鲨进行聚合搜索
    计算机组成原理---第七章输入输出系统---I/O系统基本概念,外部系统---选择题
    CVE-2022-22954-VMware Workspace ONE Access SSTI远程代码执行流量特征
    大二学生基于Html+Css+javascript的网页制作——动漫设计公司响应式网站模板 (10个页面)
    golang 函数参数传递--指针,引用和值(二)
    SQL发送邮件功能支持配置哪些SMTP服务器?
  • 原文地址:https://blog.csdn.net/qq_57404736/article/details/128058115