Java8新特性:Optional类深度解析

Optional

如果一个方法返回一个Object,那么我们在使用的时候总是要判断一下返回的结果是否为空,一般是这样的形式:

if (a != null) {
    //do something...
}

但是简单的情况还好,如果复杂的情况下每一个都要去检查非常麻烦,而且写出来的代码也不好看、很臃肿,但是如果不检查就很容易遇到NullPointerException, Java8中的Optional就是为此而设计的。

Optional一般使用在方法的返回值中,如果使用Optional来包装方法的返回值,这就表示方法的返回值可能为null,需要使用Optional提供的方法来检查,如果为null,还可以提供一个默认值。

//创建Optional对象
Optional<String> opt = Optional.empty();
 
//依据一个非空值创建Optional
Optional<String> opt = Optional.of("hello");
 
//可接受null的Optional
Optional<String> opt = Optional.ofNullable(null);

除了以上这些方法外,Optional还提供了以下方法:

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

of

为非null的值创建一个Optional。通过工厂方法创建Optional类。注意:创建对象时传入的参数不能为null。否则抛出NullPointerException 。

//调用工厂方法创建Optional实例
Optional<String> name = Optional.of("Name");

//传入参数为null,抛出NullPointerException.
Optional<String> someNull = Optional.of(null);

ofNullable

为指定的值创建一个Optional,如果指定的值为null,则返回一个空的Optional。

ofNullable与of方法相似,唯一的区别是可以接受参数为null的情况。

//下面创建了一个不包含任何值的Optional实例
//例如,值为'null'
Optional empty = Optional.ofNullable(null);

isPresent存在才对它做点什么

非常容易理解,如果值存在返回true,否则返回false。

//isPresent方法用来检查Optional实例中是否包含值
if (name.isPresent()) {
  System.out.println(name.get());   // 输出结果
}

示例2

// 推荐
user.ifPresent(System.out::println);

// 不推荐
if (user.isPresent()) {
  System.out.println(user.get());
}

ifPresent

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

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

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

// ifPresent方法接受lambda表达式作为参数。
// lambda表达式对Optional的值调用consumer进行处理。
Optional<String> name = Optional.of("Name");
name.ifPresent(value -> System.out.println("The length of the value is: " + value.length()));
System.out.println(name.get());

// 运行结果
The length of the value is: 4
Name

示例2

Optional<Student> student = Optional.of(new Student("Anson",100));
System.out.println(student.get().getName());    // Anson
student.ifPresent(stu->stu.setName("听风"));
System.out.println(student.get().getName());    // 听风

get

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

Optional empty = Optional.ofNullable(null);
System.out.println(empty.get());

// 异常内容 Exception in thread "main" java.util.NoSuchElementException: No value present

orElse 存在即返回, 无则提供默认值

如果有值则将其返回,否则返回指定的其它值。

如果Optional实例有值则将其返回,否则返回orElse方法传入的参数。示例如下:

Optional<String> name = Optional.of("听风");
Optional<String> empty = Optional.empty();

// 如果值不为null,orElse方法返回Optional实例的值。
// 如果为null,返回传入的消息。
System.out.println(empty.orElse("There is no value present!")); // There is no value present!
System.out.println(name.orElse("There is some value!"));        // 听风

orElseGet 存在即返回, 无则由函数来产生

orElseGet与orElse方法类似,区别在于得到的默认值。orElse方法将传入的字符串作为默认值,orElseGet方法可以接受Supplier接口的实现用来生成默认值。示例如下:

return user.orElseGet(() -> fetchAUserFromDatabase()); // 而不要 return user.isPresent() ? user: fetchAUserFromDatabase();

orElseThrow

如果有值则将其返回,否则抛出supplier接口创建的异常。

在orElseGet方法中,我们传入一个Supplier接口。然而,在orElseThrow中我们可以传入一个lambda表达式或方法,如果值不存在来抛出异常。示例如下:

try {
    //orElseThrow与orElse方法类似。与返回默认值不同,
    //orElseThrow会抛出lambda表达式或方法生成的异常

    empty.orElseThrow(ValueAbsentException::new);
} catch (Throwable ex) {
    //输出: No value present in the Optional instance
    System.out.println(ex.getMessage());
}

ValueAbsentException定义如下:

public class ValueAbsentException extends Throwable {

    public ValueAbsentException() {
        super();
    }

    public ValueAbsentException(String msg) {
        super(msg);
    }

    @Override
    public String getMessage() {
        return "No value present in the Optional instance";
    }
}

map

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

map方法用来对Optional实例的值执行一系列操作。通过一组实现了Function接口的lambda表达式传入操作。

map方法示例如下:

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

flatMap

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

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

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

Optional<String> name = Optional.of("hello");
Optional<String> upperName = name.flatMap(value -> Optional.of(value.toUpperCase()));
System.out.println(upperName.orElse("No value found"));     // HELLO

filter

filter个方法通过传入限定条件对Optional实例的值进行过滤。

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

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

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

Optional<String> name = Optional.of("helloWorld");
Optional<String> longName = name.filter(v -> v.length() > 6);
System.out.println(longName.orElse("名字长度小于6")); // 输出:helloWorld

Optional<String> anotherName = Optional.of("程序喵");
Optional<String> shortName = anotherName.filter(v -> v.length() > 6);
System.out.println(shortName.orElse("名字长度小于6"));    // 输出:名字长度小于6

Map其他示例

当 user.isPresent() 为真, 获得它关联的 orders , 为假则返回一个空集合时, 我们用上面的 orElse , orElseGet 方法都乏力时, 那原本就是 map 函数的责任, 我们可以这样一行

return user.map(u -> u.getOrders()).orElse(Collections.emptyList())
//上面避免了我们类似 Java 8 之前的做法
if(user.isPresent()) {
  return user.get().getOrders();
} else {
  return Collections.emptyList();
}

map 是可能无限级联的, 比如再深一层, 获得用户名的大写形式

return user.map(u -> u.getUsername())
           .map(name -> name.toUpperCase())
           .orElse(null);

这要搁在以前, 每一级调用的展开都需要放一个 null 值的判断

User user = .....
if(user != null) {
  String name = user.getUsername();
  if(name != null) {
    return name.toUpperCase();
  } else {
    return null;
  }
} else {
  return null;
}

针对这方面 Groovy 提供了一种安全的属性/方法访问操作符 ?.

user?.getUsername()?.toUpperCase();

Swift 也有类似的语法, 只作用在  Optional 的类型上.


未经允许请勿转载:程序喵 » Java8新特性:Optional类深度解析

点  赞 (2) 打  赏
分享到: