Java8 教程

Java8 Optional 类

Optional 类是 Java8 引入的一个容器对象,它可能包含也可能不包含非 null 的值(即 Optional 中可能有值,也可能没有值)。Optional 类的引入是为了解决空指针异常(NullPointerException)的问题,它提供了一种更好的方式来处理可能为 null 的值。

Optional 类的主要优点:

  • 明确表达了代码中可能存在空值的情况,增强了代码的可读性和可理解性。例如,以前可能需要通过大量的 if (obj!= null) 这样的条件判断来处理空值,使用 Optional 后可以更清晰地表明对可能为空值的处理意图。

  • 帮助避免空指针异常。若在未正确处理空值的情况下进行操作,很容易引发 NullPointerException 。Optional 类通过提供一系列方法,使得在处理空值时更加安全。

假如有如下代码:

Map<String,String> map = new HashMap<>();
map.put("key1", null);
map.put("key2", "value2");

// 使用 if 语句避免为 null,出现 NullPointerException 异常
if(null != map.get("key1")) {
    System.out.println("value is " + map.get("key1").toUpperCase());
}

if(null != map.get("key2")) {
    System.out.println("value is " + map.get("key2").toUpperCase());
}
//输出:
//value is VALUE2

当我们有了 Optional 类,就可以避免上面的 if 语句,修改后的代码如下:

Map<String,String> map = new HashMap<>();
map.put("key1", null);
map.put("key2", "value2");

// 使用 Optional 避免 if 语句
Optional.ofNullable(map.get("key1")).ifPresent(v -> {
    System.out.println("value is "+ v.toUpperCase());
});

Optional.ofNullable(map.get("key2")).ifPresent(v -> {
    System.out.println("value is "+ v.toUpperCase());
});
//输出:
//value is VALUE2

注意,如果 Optional 中存在被封装的对象,那么 get 方法会返回该对象,否则会抛出一个 NoSuchElementException 异常。如下:

Map<String,String> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", null);

// 使用 Optional 避免 if 语句
String val = Optional.ofNullable(map.get("key1")).get();
System.out.println("value is " + val.toUpperCase());

val = Optional.ofNullable(map.get("key2")).get();
System.out.println("value is " + val.toUpperCase());

//输出:
//value is VALUE1
//Exception in thread "main" java.util.NoSuchElementException: No value present
//  at java.util.Optional.get(Optional.java:135)
//  at com.hxstrive.jdk8.optional.OptionalDemo3.main(OptionalDemo3.java:22)

Optional 常用方法

创建 Optional 实例

Optional 类提供了如下三个方法去创建一个 Optional 对象:

  • 使用 Optional.of(T value) 创建一个包含非 null 值的 Optional 实例,即参数 value 不能为 null。

  • 使用 Optional.empty() 创建一个空的 Optional 实例。

  • 使用 Optional.ofNullable(T value) 创建一个可能包含 null 的 Optional 实例,即参数 value 可以为 null。

示例:

Optional.empty(); // ok
Optional.ofNullable("a"); // ok
Optional.ofNullable(null); // ok
Optional.of("key"); // ok
Optional.of(null); // error java.lang.NullPointerException

检查值是否存在

在 Optional 中,使用 isPresent() 方法检查 Optional 是否包含值。例如:

Optional opt1 = Optional.ofNullable("a");
System.out.println(opt1.isPresent() ? "有值" : "没有值"); //有值

Optional opt2 = Optional.ofNullable(null);
System.out.println(opt2.isPresent() ? "有值" : "没有值"); //没有值

上面的用法和直接使用 if 进行判断没有什么区别。

获取值

Optional 类提供了如下方法来获取值:

  • 使用 get() 方法获取值(如果 Optional 为空,则抛出 NoSuchElementException)。

  • 使用 orElse(T other) 方法获取值,如果 Optional 为空,则返回其他值。

  • 使用 orElseGet(Supplier<? extends T> other) 方法获取值,如果 Optional 为空,则调用其他值的 Supplier 函数。

  • 使用 orElseThrow(Supplier<? extends X> exceptionSupplier) 方法获取值,如果 Optional 为空,则抛出由提供的 Supplier 抛出的异常。

示例:

Optional<String> opt1 = Optional.ofNullable("value1");
Optional<String> opt2 = Optional.ofNullable(null);

System.out.println(opt1.get());
//System.out.println(opt2.get()); // NoSuchElementException 错误

// 如果 opt2 中没有值,则返回指定的默认值
// 如果有值,则什么也不做
System.out.println(opt1.orElse("default1"));
System.out.println(opt2.orElse("default2")); // default2

// 如果 opt2 中没有值,则执行提供的 lambda 表达式,返回 default3 字符串
System.out.println(opt2.orElseGet(() -> "default3")); // default3

// 如果 opt2 中没有值,则抛出 java.lang.IllegalStateException 异常
System.out.println(opt2.orElseThrow(() -> new IllegalStateException()));

转换值

Optional 类提供了如下两个方法,用来对值进行转换:

  • 使用 map(Function<? super T, ? extends U> mapper) 方法对值进行转换。如果存在一个值,则对其应用所提供的映射函数(Function),如果结果为非空,则返回一个描述结果的 Optional 。否则返回一个空的 Optional 。

  • 使用 flatMap(Function<? super T, Optional<U>> mapper) 方法进行链式转换。如果存在一个值,则对其应用所提供的带有 Optional 的映射函数,并返回该结果,否则返回一个空的 Optional。该方法类似于 map(Function),但所提供的映射器的结果已经是一个 Optional,如果调用该映射器,flatMap 不会用额外的 Optional 对其进行包装。

注意:如果 Optional 没有值(即 isPresent() 返回 false),则不会调用 map 和 flatMap 方法。

示例:

Optional<String> opt1 = Optional.ofNullable("value1");
// map 方法会返回一个新的
String val1 = opt1.map(v -> {
    System.out.println("opt1 = " + v);
    return v.toUpperCase();
}).orElse("");
System.out.println("val1 = " + val1);


Optional<String> opt2 = Optional.ofNullable(null);
String val2 = opt2.map(v -> {
    System.out.println("opt2 = " + v);
    return v.toUpperCase();
}).orElse("");
System.out.println("val2 = "+ val2);
//输出:
//opt1 = value1
//val1 = VALUE1
//val2 =

flatMap 示例:

Optional<String> opt1 = Optional.ofNullable("value1");
String val1 = opt1.flatMap(v -> {
    System.out.println("flatMap1 = " + v);
    return Optional.of(v.toUpperCase());
}).flatMap(v -> {
    System.out.println("flatMap2 = " + v);
    return Optional.of("[" + v + "]");
}).orElse("");
System.out.println("val1 = " + val1);
//输出:
//flatMap1 = value1
//flatMap2 = VALUE1
//val1 = [VALUE1]

过滤值

Optional 类使用 filter(Predicate<? super T> predicate) 方法根据条件过滤值。如果存在一个值,且该值符合给定的谓词(Predicate),则返回一个描述该值的 Optional ,否则返回一个空的 Optional (即 isPresent 方法为 false)。例如:

Optional<String> opt1 = Optional.ofNullable("value1");
String val1 = opt1.filter(x -> x.equals("value1")).orElse("-");
System.out.println("val1 = " + val1);

String val2 = opt1.filter(x -> x.equals("value2")).orElse("-");
System.out.println("val2 = " + val2);
//输出:
//val1 = value1
//val2 = -

使用 Optional 值

高效使用 Optional 的关键在于,使用一个或者接受正确值、或者返回另一个替代值的方法。

ifPresent 方法的另一种形式可以接受一个函数。如果存在可选值,那么它会将该值传递给函数,否则不会进行任何操作:

optionalValue.ifPresent(v -> Process v);
// 类似
if(optional.isPresent()) {
    //...进行操作...
}

如果你希望当值存在时将它添加到一个集合中,可以如下:

final List<String> results = new ArrayList<>();
optionalValue.ifPresent(v -> results.add(v));

或者更简单一点:

optionalValue.ifPresent(results::add);

当调用这种形式的 ifPresent 方法时,不会返回任何值。如果你希望对结果进行处理,可以使用 map 方法:

Optional<Boolean> added = optionalValue.map(results::add);

现在 added 有可能是以下三种值:

(1)被封装到 Optional 中的 true。

(2)被封装到 Optional 中的 false。

(3)一个空的可选值。

注意:这个 map 方法类似于 Stream 接口的 map 方法。你可以简单地将一个可选值想象为一个大小为 0 或 1 的流,返回结果的大小也是 0 或者 1。

你已经了解了当一个可选值存在时应该如何对它优雅地进行处理。另一种使用可选值的方式是,当没有值存在时,产生一个替代值(即默认值)。通常,当没有可匹配项时,你会希望使用一个默认值,例如一个空字符串:

// 如果 Optional 中有值,则返值;否则,返回 orElse 指定的默认值。
Optional<String> opt1 = Optional.ofNullable("value1");
System.out.println(opt1.orElse("-")); // value1

Optional<String> opt2 = Optional.ofNullable(null);
System.out.println(opt2.orElse("default value")); // default value

你还可以调用代码来计算默认值:

// 如果 Optional 中有值,则返值;否则,调用 orElseGet 方法指定的函数,使用函数获取默认值
String result = optionalString.orElseGet(() -> System.getProperty("user.dir"));

或者,如果你希望在没有值的时候抛出另一个异常,如下:

// 如果 Optional 中有值,则返值;否则,调用 orElseThrow 方法指定的函数,抛出一个异常
String result = optionalString.orElseThrow(NoSuchElementException::new);

使用 flatMap 来组合可选值函数

假设你有一个会返回 Optional<T> 的方法 f,并且目标类型 T 有一个会返回 Optional<U> 的方法 g。如果它们都是普通的方法,你可能会考虑通过调用 s.f().g() 将它们组合起来。但是这种组合在这里是行不通的。因为 s.f() 方法返回的是 Optional<T>,而不是T。但是,我们可以调用:

Optional<U> = s.f().flatMap(T::g);

如果 s.f() 存在,那么会继续调用 g。否则,会返回一个空的 Optional<U>。

显然,如果你有更多的返回 Optional 值的方法或表达式,你可以重复这个步骤,将它们都组合起来。然后你只需要通过不断调用 flatMap,就可以创建一个调用的流水线,只有当其中每个部分都成功时,整个流水线才返回成功。

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号