根据Oracle文档,Optional是一个容器对象,可以包含也可以不包含非null值。Optional在Java 8中引入,目的是解决 NullPointerExceptions的问题。本质上,Optional是一个包装器类,其中包含对其他对象的引用。在这种情况下,对象只是指向内存位置的指针,并且也可以指向任何内容。从其它角度看,Optional提供一种类型级解决方案来表示可选值而不是空引用。

一、Optional出现之前

在Java 8之前,程序员将返回null而不是Optional。这种方法有一些缺点。一种是没有明确的方法来表示null可能是一个特殊值。相比之下,在API中返回Optional是明确的声明,其中可能没有值。如果我们要确保不会出现空指针异常,则需要对每个引用进行显式的空检查,如下所示,我们都同意这是很多样板。

  1. // Life before Optional
  2. private void getIsoCode( User user){
  3. if (user != null) {
  4. Address address = user.getAddress();
  5. if (address != null) {
  6. Country country = address.getCountry();
  7. if (country != null) {
  8. String isocode = country.getIsocode();
  9. if (isocode != null) {
  10. isocode = isocode.toUpperCase();
  11. }
  12. }
  13. }
  14. }
  15. }

为了简化此过程,让我们看一下如何使用Optional类,从创建和验证实例到使用它提供的不同方法并将其与返回相同类型的其他方法组合在一起,后者才是Optional的厉害之处。

二、创建Optional

Optional类提供了大约10种方法,我们可以使用它们来创建和使用Optional类,下面将介绍如何使用它们。

1:empty( )方法

返回一个空的Optional实例。注意该方法是泛型方法,如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法。即使静态方法要使用泛型类中已经声明过的泛型也不可以。

java8 Optional - 图1

java8 Optional - 图2

示例代码:

  1. Optional<Insurance> insuranceOptional = Optional.empty();

在返回一个空的{Optional}实例时,Optional的值不存在。

2:of方法,返回特定的非空值Optional。

java8 Optional - 图3

new Optional<>(value)的时候会进行空值的判断,因此静态方法需要穿一个非null的参数,否则,将引发空指针异常。因此,如果我们不知道参数是否为null,那就是我们使用 ofNullable的时候,下面将对此进行介绍。

java8 Optional - 图4

3:ofNullable,返回描述指定值的Optional,如果空,则返回空值。

java8 Optional - 图5

获取Optional中的值

1: orElse

java8 Optional - 图6

返回值(如果存在);反之,返回其他。

该 orElse() 方法用于检索包装在Optional实例内的值。它采用一个充当默认值的参数。该 orElse() 方法返回包装的值(如果存在)及其参数,反之:

  1. //orElse
  2. String nullName = null;
  3. String name = Optional.ofNullable(nullName).orElse("default_name");

2:orElseGet

java8 Optional - 图7

返回值(如果存在);否则,调用other并返回该调用的结果。

该orElseGet() 方法类似于 orElse()。但是,如果没有Optional值,则不采用返回值,而是采用供应商功能接口,该接口将被调用并返回调用的值:

  1. //orElseGet
  2. String name = Optional.ofNullable(nullName).orElseGet(() -> "john");

3:orElseThrow

如果存在返回容器中的值,如果不存在抛出一个异常值

java8 Optional - 图8

三、检查值的存在

1:isPresent

如果值存在返回true,否则返回false。

java8 Optional - 图9

类型与下面的代码:

  1. //isPresent方法用来检查Optional实例中是否包含值
  2. if (name.isPresent()) {
  3. //在Optional实例内调用get()返回已存在的值
  4. System.out.println(name.get());//输出Sanaulla
  5. }

2:get

如果Optional有值则将其返回,否则抛出NoSuchElementException。

上面的示例中,get方法用来得到Optional实例中的值。下面我们看一个抛出NoSuchElementException的例子:

  1. //执行下面的代码会输出:No value present
  2. try {
  3. //在空的Optional实例上调用get(),抛出NoSuchElementException
  4. System.out.println(empty.get());
  5. } catch (NoSuchElementException ex) {
  6. System.out.println(ex.getMessage());
  7. }

3:ifPresent

如果Optional实例有值则为其调用consumer,否则不做处理

要理解ifPresent方法,首先需要了解Consumer类。简答地说,Consumer类包含一个抽象方法。该抽象方法对传入的值进行处理,但没有返回值。Java8支持不用接口直接通过lambda表达式传入参数。

如果Optional实例有值,调用ifPresent()可以接受接口段或lambda表达式。类似下面的代码:

  1. //ifPresent方法接受lambda表达式作为参数。
  2. //lambda表达式对Optional的值调用consumer进行处理。
  3. name.ifPresent((value) -> {
  4. System.out.println("The length of the value is: " + value.length());
  5. });

四、map flatMap filter的用法

map

map方法文档说明如下:

如果有值,则对其执行调用mapping函数得到返回值。如果返回值不为null,则创建包含mapping返回值的Optional作为map方法返回值,否则返回空Optional。

map方法用来对Optional实例的值执行一系列操作。通过一组实现了Function接口的lambda表达式传入操作。如果你不熟悉Function接口,可以参考这篇博客](http://blog.sanaulla.info/2013/03/27/function-interface-a-functional-interface-in-the-java-util-function-package-in-java-8/)。map方法示例如下:

  1. // map方法执行传入的lambda表达式参数对Optional实例的值进行修改。
  2. // 为lambda表达式的返回值创建新的Optional实例作为map方法的返回值。
  3. Optional<String> upperName = name.map((value) -> value.toUpperCase());
  4. System.out.println(upperName.orElse("No value found"));

flatMap

如果有值,为其执行mapping函数返回Optional类型返回值,否则返回空Optional。flatMap与map(Funtion)方法类似,区别在于flatMap中的mapper返回值必须是Optional。调用结束时,flatMap不会对结果用Optional封装。

flatMap方法与map方法类似,区别在于mapping函数的返回值不同。map方法的mapping函数返回值可以是任何类型T,而flatMap方法的mapping函数必须是Optional。

参照map函数,使用flatMap重写的示例如下:

  1. // flatMap与map(Function)非常类似,区别在于传入方法的lambda表达式的返回类型。
  2. // map方法中的lambda表达式返回值可以是任意类型,在map函数返回之前会包装为Optional。
  3. // 但flatMap方法中的lambda表达式返回值必须是Optionl实例。
  4. upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
  5. System.out.println(upperName.orElse("No value found"));//输出SANAULLA

filter

filter个方法通过传入限定条件对Optional实例的值进行过滤。文档描述如下:

如果有值并且满足断言条件返回包含该值的Optional,否则返回空Optional。

读到这里,可能你已经知道如何为filter方法传入一段代码。是的,这里可以传入一个lambda表达式。对于filter函数我们应该传入实现了Predicate接口的lambda表达式。如果你不熟悉Predicate接口,可以参考这篇文章

现在我来看看filter的各种用法,下面的示例介绍了满足限定条件和不满足两种情况:

  1. // filter方法检查给定的Option值是否满足某些条件。
  2. // 如果满足则返回同一个Option实例,否则返回空Optional。
  3. Optional<String> longName = name.filter((value) -> value.length() > 6);
  4. System.out.println(longName.orElse("The name is less than 6 characters"));// 输出Sanaulla
  5. // 另一个例子是Optional值不满足filter指定的条件。
  6. Optional<String> anotherName = Optional.of("Sana");
  7. Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
  8. // 输出:name长度不足6字符
  9. System.out.println(shortName.orElse("The name is less than 6 characters"));

参考文档:

Java 8 Optional类深度解析](https://wizardforcel.gitbooks.io/java8-tutorials/content/Java 8 Optional 类深度解析.html))