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
}

Optional.empty()

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

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
}

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
}

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值,不执行里面函数,无结果
}

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
}

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
}

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
}

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"));
}

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
}

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
}

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));
}

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;
    }
}

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;
    }
}

(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();
        }
    }
}

3、总结

  • null引用在历史上被引入到程序设计语言中,目的是为了表示变量值的缺失
  • Java 8中引入了一个新的类java.util.Optional,对存在或缺失的变量值进行建模
  • 你可以使用静态工厂方法Optional.empty、Optional.of以及Optional.ofNullable创建Optional对象
  • Optionial类支持多种方法、如map、flatMap、filter它们在概念上与Stream类中对应的方法十分相似
  • 使用Optional会迫使你更积极地了解引用Optional对象,以应对变量缺失的问题,最终,你能更有效地防止代码中出现不期而至的空指针异常。
  • 使用Optional能帮助你设计更好的API,用户只需要阅读方法签名,就能了解方法是否接收一个Optional类型的值

你可能感兴趣的:(java,开发语言)