欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JDK8新特性03:Optional类-空指针异常的预防手段

程序员文章站 2022-01-15 12:00:52
...

Optional类概述

Optional对象是Java8新引入的类,用来预防Java中臭名昭著的NullPointerException问题.阅读Optional类的文档如下:

A container object which may or may not contain a non-null value. If a value is present, isPresent() returns true. If no value is present, the object is considered empty and isPresent() returns false.

Additional methods that depend on the presence or absence of a contained value are provided, such as orElse(Object) (returns a default value if no value is present) and ifPresent(Consumer(performs an action if a value is present).

Optional对象是一个容器对象,存放的可以是空或非空对象.通过调用其实例方法isPresent()方法判断其中存放对象是否为空:若存放的是非空值则返回true,否则返回false.

This is a value-based class; use of identity-sensitive operations (including reference equality (==), identity hash code, or synchronization) on instances of Optional may have unpredictable results and should be avoided.

这是一个value-based类,我们只需要考虑其中所储存的值,而不应该将其当成一般的对象来看待.对Optional对象进行任何id敏感的操作(包括:判断引用相等==,计算hashcode或同步)都会产生不可预知的结果,应该避免这种操作.

Optional的使用

Optioinal对象的创建与基本使用

在文档中将Optional类称为value-based类,value-based的一个特征就是没有公共的构造方法,而是通过工厂方法创建.

Optional对象有三个工厂方法:

  • empty():构造一个包含空对象的Optional对象(注意该Optional对象本身不是null,但是它存放的是null.当然,考察对象是否为null就已经脱离了value-based类的本意).
  • of(T value):构造一个包含value对象的Optional对象,若传入的value为空则报错.
  • ofNullable(T value):根据传入的value是否为空判断返回什么类型的Optional对象:若传入的value为空,则返回包含空对象的Optional对象,否则返回包含value对象的Optional对象.

除此之外,Optional对象还有两个实例方法:

  • isPresent():判断该Optional对象中包含的是否为非空对象.
  • get():取出该Optional对象中包含的对象,若该Optional对象包含的null,则报错NoSuchElementException.

使用Optional的上述方法时,与传统的编程方式并无不同,要遵循先判空后操作的规则.(实际上,取出Optional中对象的更常用方法是orElseXXX())

Optional<String> optional = Optional.of("hello");
if (optional.isPresent()) {
    System.out.println(optional.get());
}

Optional的函数式编程接口

Optional对象中的几个方法提供了对函数式编程的支持:

  • ifPresent(Consumer action):若该Optional对象中存储的是非空对象,则调用消费者action来消费.
  • map(Function mapper):若该Optional对象中存储的是非空对象,则将对被储存对象调用mapper中方法得到的返回值存入Optional对象并返回;否则返回一个保存nullOptional对象.
  • orElseXXX()一系列方法:若该Optional对象中存储的是非空对象,返回被存储的对象;否则根据方法不同执行不同的操作
    1. orElse(T other):若该Optional对象中存储的是非空对象,返回被存储的对象;否则若返回other.
    2. orElseGet(Supplier supplier):若该Optional对象中存储的是非空对象,返回被存储的对象;否则返回从生产者supplier中取出的对象.
    3. orElseThrow(Supplier exceptionSupplier):若该Optional对象中存储的是非空对象,返回被存储的对象;否则抛出从异常生产者exceptionSupplier中取出的异常.

这些方法的区别:ifPresent()直接消费被储存的对象,map()根据储存的对象创建新的Optional对象,orElse()方法取出被储存的对象.

使用上述方法,可以利用实现对Optional对象的函数式编程.

Optional<String> optional = Optional.empty();

optional.ifPresent(item -> System.out.println(item));	// 不会有任何输出

optional.orElse("hello world");			// 返回"hello world"
optional.orElseGet(() -> "hello world");// 返回"hello world"
optional.orElseThrow();					// 抛出NoSuchElementException异常

使用Optional的正确姿势

下面以一个例子演示如何使用Optional避免空指针异常,假设有如下的类

// 员工类
class Employee {
    // Employee类的各种属性...
}

// 公司类,包含员工列表
class Company {

    // 公司类与员工类是一对多的关系
    private List<Employee> employees;
    
    public List<Employee> getEmployees() { return employees; }

    // Company类的其他属性...
}

在Java8以前,我们要想遍历一个Company类的所有Employee时,需要如下操作:

// 模拟从数据库中取出Company对象
Company company = getCompanyFromDatabase();

// 遍历该Company对象的所有Employee,需要对company和employees都判空再操作
if (company != null) {
    List<Employee> employees = company.getEmployees();
    if (employees != null) {
        employees.forEach(employee -> System.out.println(employee));
    }
}

使用Optional的函数式编程接口,我们可以将判空和操作写在同一行

List<Employee> employees = Optional.ofNullable(company).// 若company为空,则返回空Optional
    map(theCompany -> theCompany.getEmployees()).		// 若getEmployee()返回空,则返回空Optional
    orElse(Collections.EMPTY_LIST);						// 若上述两步中有一步为空,则返回空列表

// 遍历员工列表employees
employees.forEach(employee -> System.out.println(employee));

使用Optional类要注意的问题

阅读Optional类的文档内容如下:

Optional is primarily intended for use as a method return type where there is a clear need to represent “no result,” and where using null is likely to cause errors. A variable whose type is Optional should never itself be null; it should always point to an Optional instance.

Optional的主要用途:当某个方法需要明确表示返回值可能为空时,通过将其封装成为Optional对象以简化操作并规避空指针异常.Optional引用本身永远不应该为null,而应该永远指向一个Optional对象.

这也就是说,我们永远不应当将Optional对象声明为方法参数类的成员.