Java新特性之Optional类超详细介绍

 更新时间:2023年07月25日 09:54:45   作者:栗筝i  
这篇文章主要给大家介绍了关于Java新特性之Optional类超详细介绍的相关资料,Java8中的Optional类是一个容器对象,可以包含null或非null值,文中通过示例代码介绍的非常详细,需要的朋友可以参考下

前言

Optional 类是 Java 8 才引入的,Optional 是个容器,它可以保存类型 T 的值,或者仅仅保存 null。Optional 提供了很多方法,这样我们就不用显式进行空值检测。Optional 类的引入很好的解决空指针异常。

Java 8 引入 Optional 类,用来解决 NullPointerException。 Optional 代替 if…else 解决空指针问题,使代码更加简洁。

1、Optional类概述

1.1、Optional类介绍

Optional 类是 Java 8 才引入的,Optional 是个容器,它可以保存类型 T 的值,或者仅仅保存 null。Optional 提供了很多方法,这样我们就不用显式进行空值检测。Optional 类的引入很好的解决空指针异常。

Java 8 引入 Optional 类,用来解决 NullPointerException。 Optional 代替 if…else 解决空指针问题,使代码更加简洁。

1.2、Java8之前的空指针异常判断

Java 在使用对象过程中,访问任何方法或属性都可能导致 NullPointerException:

例如我们通过以下方法,获取存在 student 对象中的 Age 值。

        public String getIsocode (Student student){
            return student.getAge();
        }

在这样的示例中,如果我们想要避免由 studentstudent.age 为空而导致的空指针问题,我们就需要采用防御式检查减少 NullPointerException(在访问每一个值之前对其进行明确地检查):

       public String getIsocode (Student student){
            if (null == student) {
                // doSomething
                return "Unknown";
            }
            if (null = student.getAge()) {
                // doSomething
                return "Unknown";
            }
            return student.getAge();
        }

然而,这种方案并不是很理想,因为为此会多出多个不同的退出点(return),使得代码维护变得艰难,之后每个可能的 null 检查都会新增一个退出点。

为了简化这个过程,我们来看看用 Optional 类是怎么做的。

1.3、Java8之后Optional的使用

当需要判断的量多时,此时的这些判断语句可能会导致代码臃肿冗余,为此 Java8 特意推出了 Optional 类来帮助我们去处理空指针异常。

下面是 Optional 的一些基本用法:

@Data
public class Student {
    private Integer age;
}
---
public class Test {
    public static void main(String[] args) {
        // 假设 student 这个对象从数据库中查出的
        Student student = getStudent();
        // 创建一个可接受 null 的 Optiona l类
        Optional<Student> optional = Optional.ofNullable(student);
        // 用法1:获取 student 对象中的某个值,如果不存在的话则取默认值(不具有短路作用)
        Integer a1 = optional.map(Student::getAge).orElse(20));
        // 用法2:获取 student 对象中的某个值,如果不存在的话则取默认值(具有短路作用,因为是懒加载)
        Integer a2 = optional.map(Student::getAge).orElseGet(() -> Integer.MAX_VALUE);
      	// 用法3:判断对象是否存在,不存在则抛出异常
        optional.orElseThrow(() -> new RuntimeException("student不存在!"));
      	// 用法4:判断对象是否存在,存在的话对对象进行操作,例如给对象赋初始值
        optional.ifPresent(o -> o.setAge(18));
      	// 用法5:对象存在时,且年龄满足一定条件容器才会继续保存这对象,否则将会剔除
        optional.filter(o -> o.getAge() > 10);
    }
}

2、Optional类使用

2.1、Optional类常用方法总结

方法描述
empty返回一个空的 Optional 实例
filter如果值存在并且满足提供的谓词,就返回包含该值的 Optional 对象;否则返回一个空的 Optional 对象
flatMap如果值存在,就对该值执行提供的 mapping 函数调用,返回一个 Optional 类型的值,否则就返回一个空的 Optional 对象
get如果该值存在,将该值用 Optional 封装返回,否则抛出一个 NoSuchElementException 异常
ifPresent如果值存在,就执行使用该值的方法调用,否则什么也不做
isPresent如果值存在就返回 true,否则返回 false
map如果值存在,就对该值执行提供的mapping 函数调用
of将指定值用 Optional 封装之后返回,如果该值为 null,则抛出一个 NullPointerException 异常
ofNullable将指定值用 Optional 封装之后返回,如果该值为 null,则返回一个空的 Optional 对象
orElse如果有值则将其返回,否则返回一个默认值
orElseGet如果有值则将其返回,否则返回一个由指定的 Supplier 接口生成的值
orElseThrow如果有值则将其返回,否则抛出一个由指定的 Supplier 接口生成的异常

2.2、Optional对象创建

2.2.1、Optional.empty()方法

使用 Optional.empty() 方法声明一个空的 Optional:

// 通过静态工厂方法 Optional.empty(),创建一个空的 Optional 对象
Optional<Student> optStudent = Optional.empty();

2.2.2、Optional.of(T t)方法

使用 Optional.of(T t) 方法创建一个包含非空值的 Optional 对象 (不推荐):

// 静态工厂方法 Optional.of(T t),依据一个非空值创建一个 Optional 对象
Optional<Student> optStudent = Optional.of(student);

如果 student 为 null,这段代码会立即抛出一个 NullPointerException,而不是等到访问 student 的属性值时才返回一个错误。

2.2.3、Optional.ofNullable(T t)方法

使用 Optional.ofNullable(T t) 方法创建一个包含可能为空的值的 Optional 对象 (推荐):

// 用静态工厂方法 Optional.ofNullable(T t),你可以创建一个允许 null 值的 Optional 对象
Optional<Student> optStudent = Optional.ofNullable(student);

2.3、Optional对象获取

2.3.1、get()方法

get() 方法,如果变量存在,它直接返回封装的变量值,否则就抛出一个 NoSuchElementException 异常,不推荐使用:

optional.map(Student::getAge).get()

2.3.2、orElse(T other)方法

orElse(T other) 方法,它允许你在 Optional 对象不包含值时提供一个默认值:

optional.map(Student::getAge).orElse(20));

2.3.3、orElseGet(Supplier<? extends T> other)方法

orElseGet(Supplier<? extends T> other) 方法,它是 orElse 方法的延迟调用版,Supplier 方法只有在 Optional 对象不含值时才执行调用(懒加载):

optional.map(Student::getAge).orElseGet(() -> Integer.MAX_VALUE);

2.3.4、orElseThrow(Supplier<? extends X> exceptionSupplier)方法

orElseThrow(Supplier<? extends X> exceptionSupplier) 方法,它和 get 方法非常类似,它们遭遇 Optional 对象为空时都会抛出一个异常,但是使用 orElseThrow 可以定制希望抛出的异常类型:

optional.orElseThrow(() -> new RuntimeException("student不存在!"));

2.3.5、ifPresent(Consumer<? super T> consumer)方法

ifPresent(Consumer<? super T> consumer) 方法,它让能在变量值存在时执行一个作为参数传入的方法,否则就不进行任何操作:

optional.ifPresent(o -> o.setAge(18));

2.4、Optional对象中值的提取和转换

2.4.1、map()方法

map() 方法,如果值存在,就对该值执行提供的 mapping 函数调用,如果值不存在,则返回一个空的 Optional 对象。

引入 Optional 以前:

String name = null;
    if(insurance != null){
        name = insurance.getName();
    }

引入 Optional 以后:

Optional<String> name = Optional.ofNullable(insurance).map(Insurance::getName);

Optional 的 map 方法和 Java 8 中 Stream 的 map 方法相差无几。

2.4.2、flatMap()方法

flatMap() 方法,对于嵌套式的 Optiona 结构,我们应该使用 flatMap 方法,将两层的 Optional 合并成一个。

我们试着重构以下代码:

  public String getCarInsuranceName(Person person) {
      return person.getCar().getInsurance().getName();
  }

由于我们刚刚学习了如何使用 map,我们的第一反应可能是我们可以利用 map 重写之前的代码:

  Optional<Person> optPerson = Optional.of(person);
      Optional<String> name =
          optPerson.map(Person::getCar)
                   .map(Car::getInsurance)
                   .map(Insurance::getName);

不幸的是,这段代码无法通过编译。为什么呢? optPerson 是 Optional<Person> 类型的 变量, 调用 map 方法应该没有问题。但 getCar 返回的是一个 Optional<Car> 类型的对象,这意味着 map 操作的结果是一个 Optional<Optional<Car>> 类型的对象。因此,它对 getInsurance 的调用是非法的。

下面应用 map 和 flatMap 对上述示例进行重写:

  public String getCarInsuranceName(Optional<Person> person) { 
      return person.flatMap(Person::getCar)
                       .flatMap(Car::getInsurance)
                       .map(Insurance::getName)
                       .orElse("Unknown"); // 如果Optional的结果 值为空设置默认值
  }

2.5、Optional对象其他方法

2.5.1、isPresent()方法

可以使用 isPresent() 方法检查 Optional 对象是否包含非空值,例如:

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

2.5.2、filter()方法

filter() 方法接受一个谓词作为参数。如果 Optional 对象的值存在,并且它符合谓词的条件,filter 方法就返回其值,否则它就返回一个空的 Optional 对象。

比如,你可能需要检查保险公司的名称是否为 “Cambridge-Insurance”。

Insurance insurance = ...;
	if(insurance != null && "CambridgeInsurance".equals(insurance.getName())){
			System.out.println("ok");
  }

使用 Optional 对象的 filter 方法,这段代码可以重构如下:

Optional<Insurance> optInsurance = ...;
optInsurance.filter(insurance -> "CambridgeInsurance".equals(insurance.getName()))
            .ifPresent(x -> System.out.println("ok"));

3、Optional注意事项

3.1、Optional的序列化问题

由于 Optiona l类设计时就没特别考虑将其作为类的字段使用,所以它也并未实现 Serializable 接口。由于这个原因,如果你的应用使用了某些要求序列化的库或者框架,在域模型中使用Optional,有可能引发应用程序故障。

然而,我们相信,通过前面的介绍,我们已经看到用 Optional 声明域模型中的某些类型是个不错的主意,尤其是你需要遍历有可能全部或部分为空,或者可能不存在的对象时。如果你一定要实现序列化的域模型,作为替代方案, 我们建议你像下面这个例子那样,提供一个能访问声明为 Optional、变量值可能缺失的接口,代码清单如下:

public class Person {
    private Car car;
    public Optional<Car> getCarAsOptional() {
      return Optional.ofNullable(car);
    } 
}

3.2、避免使用基础类型的 Optional 对象

Optional 提供了的一些基础类型 —— OptionalIntOptionalLong 以及 OptionalDouble ,但不推荐大家使用基础类型的 Optional,因为基础类型的 Optional 不支持 map、 flatMap 以及 filter 方法,而这些却是 Optional 类常用的方法。可以使用 Optional<Int>Optional<Long>Optional<Double> 等替代。3. orElse方法的使用

3.3、orElse方法的使用

orElse 中调用的方法一直都会被执行,orElseGet 方法只有在 Optional 对象不含值时才会被调用,所以使用 orElse 方法时需要谨慎, 以免误执行某些不被预期的操作。此种情况下,可使用 orElseGet 方法代替它。

总结

到此这篇关于Java新特性之Optional类超详细介绍的文章就介绍到这了,更多相关Java新特性Optional类内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot log打印及输出方式

    SpringBoot log打印及输出方式

    这篇文章主要介绍了SpringBoot log打印及输出方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • spring boot自定义log4j2日志文件的实例讲解

    spring boot自定义log4j2日志文件的实例讲解

    下面小编就为大家分享一篇spring boot自定义log4j2日志文件的实例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2017-11-11
  • Java数据结构之加权无向图的设计实现

    Java数据结构之加权无向图的设计实现

    加权无向图是一种为每条边关联一个权重值或是成本的图模型。这种图能够自然地表示许多应用。这篇文章主要介绍了加权无向图的设计与实现,感兴趣的可以了解一下
    2022-11-11
  • windows 部署JAVA环境安装iDea的详细步骤

    windows 部署JAVA环境安装iDea的详细步骤

    这篇文章主要介绍了windows 部署JAVA环境安装iDea的详细步骤,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • SpringBoot集成logback打印彩色日志的代码实现

    SpringBoot集成logback打印彩色日志的代码实现

    Logback是由log4j创始人设计的另一个开源日志组件,默认情况下,Spring Boot会用Logback来记录日志,并用INFO级别输出到控制台,本文给大家介绍了SpringBoot集成logback打印彩色日志,需要的朋友可以参考下
    2024-03-03
  • 一篇文中细看Java多线程的创建方式

    一篇文中细看Java多线程的创建方式

    随着计算机的配置越来越高,我们需要将进程进一步优化,细分为线程,充分提高图形化界面的多线程的开发,这篇文章主要给大家介绍了如何通过一篇文中细看Java多线程的创建方式,需要的朋友可以参考下
    2021-07-07
  • Java中使用BigDecimal进行精确运算

    Java中使用BigDecimal进行精确运算

    这篇文章主要介绍了Java中使用BigDecimal进行精确运算的方法,非常不错,需要的朋友参考下
    2017-02-02
  • Spring学习之Bean的装配多种方法

    Spring学习之Bean的装配多种方法

    本篇文章主要介绍了Spring学习之Bean的装配三种方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • mybatisPlus更新字段值为null的解决方案

    mybatisPlus更新字段值为null的解决方案

    在使用mybatis-plus时,发现当前端传入的值为null值时,结果无论怎么操作后端都不执行更新null字段的操作,下面这篇文章主要给大家介绍了关于mybatisPlus更新字段值为null的解决方案,需要的朋友可以参考下
    2023-04-04
  • java基础之泛型知识点总结

    java基础之泛型知识点总结

    这篇文章主要介绍了java基础之泛型知识点总结,文中有非常详细的代码示例,对正在学习java基础的小伙伴们有很好的帮助,需要的朋友可以参考下
    2021-04-04

最新评论