Java8之Optional

zlong.w Lv2

1. 简介

Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException)

本质上,这是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空。

Optional 是 Java 实现函数式编程的强劲一步,并且帮助在范式中实现。

2. 类方法

a. 创建Optional方法
方法 作用
Optional.of(T t) 创建一个 Optional 对象,参数 t 必须非空,
如果你把null 值作为参数传递进去,
of()方法会抛出NullPointerException
Optional.empty() 创建一个空的Optional实例
Optional.ofNullable(T t) 创建一个 Optional 对象,如果参数 t 为非空,
返回 Optional 描述的指定值,
否则返回空的 Optional
b. 判断Optional容器是否包含对象
方法 作用
boolean isPresent() 如果值存在则方法会返回 true,否则返回 false。
void ifPresent(Consumer<? super T> consumer) 如果值存在则使用该值调用 consumer , 否则不做任何事情。
c. 获取Optional容器对象
方法 作用
T get() 如果在这个 Optional 中包含这个值,返回值,
否则抛出异常:NoSuchElementException
T orElse(T other) 如果存在该值,返回值, 否则返回 other。
T orElseGet(Supplier<? extends T> other) 如果存在该值,返回值, 否则触发 other,并返回 other 调用的结果。
T
orElseThrow(Supplier<? extends X> exceptionSupplier)
如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常
d. 过滤转换值
方法 作用
Optional filter(Predicate<? super predicate) 如果值存在,并且这个值匹配给定的 predicate,返回一个
Optional用以描述这个值,否则返回一个空的Optional
Optional flatMap(Function<? super T,Optional> mapper) 如果值存在,返回基于Optional包含的映射方法的值,否则返回一个空的Optional
Optional map(Function<? super T,? extends U> mapper) 如果有值,则对其执行调用映射函数得到返回值。如果
返回值不为 null,则创建包含映射返回值的Optional
作为map方法返回值,否则返回空Optional。
e. 其他方法
方法 作用
boolean equals(Object obj) 判断其他对象是否等于 Optional。
int hashCode() 返回存在值的哈希码,如果值不存在 返回 0。
String toString() 返回一个Optional的非空字符串,用来调试

在Java8之前,任何访问对象方法或属性的调用都可能导致NullPointerException:

1
2
3
4
5
6
public class TestOptional{
@Test(expected = NoSuchElementException.class)
public void whenCreateEmptyOptional_thenNull() {
String str = user.getAddress().getCountry().getStr().toUpperCase();
}
}

如果要确保不触发异常就得在访问每一个值之前对其进行明确地检查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class TestOptional{
@Test(expected = NoSuchElementException.class)
public void whenCreateEmptyOptional_thenNull() {
if (user != null) {
Address address = user.getAddress();
if (address != null) {
Country country = address.getCountry();
if (country != null) {
String str = country.getStr();
if (str != null) {
str = str.toUpperCase();
}
}
}
}
}
}

Optional:

2. 创建Optional

a. 创建一个空的Optional
1
2
3
4
5
6
7
public class TestOptional{
@Test(expected = NoSuchElementException.class)
public void whenCreateEmptyOptional_thenNull(){
Optional<User> emptyOpt = Optional.empty();
emptyOpt.get();
}
}

毫不奇怪,尝试访问 emptyOpt 变量的值会导致 NoSuchElementException。

b. 使用of()和ofNullable()方法创建包含值的Optional

两个方法的不同之处在于:

  • 如果of() 方法的参数值为 null ,会抛出 NullPointerException,并没有完全摆脱 NullPointerException。因此,你应该明确对象不为 null 的时候使用 of()
    1
    2
    3
    4
    5
    6
    public class TestOptional{
    @Test(expected = NoSuchElementException.class)
    public void whenCreateEmptyOptional_thenNull(){
    Optinal<User> opt = Optional.of(user);
    }
    }
  • 如果对象即可能是null也可能是非null,就应该使用ofNullable()方法
    1
    2
    3
    4
    5
    6
    public class TestOptional{
    @Test(expected = NoSuchElementException.class)
    public void whenCreateEmptyOptional_thenNull(){
    Optinal<User> opt = Optional.ofNullable(user);
    }
    }

3. 访问Optional对象的值

从Optional实例取回实际值对象的方法之一是使用get()方法

1
2
3
4
5
6
7
8
public class TestOptional{
@Test(expected = NoSuchElementException.class)
public void whenCreateEmptyOptional_thenNull(){
String name = "John";
Optinal<User> opt = Optional.ofNullable(name);
assertEquals("John",opt.get());
}
}

不过,这个方法会在值为null的时候抛出异常,需要先验证是否有值

a. 使用isPresent()方法验证是否有值
1
2
3
4
5
6
7
8
9
public class TestOptional{
@Test(expected = NoSuchElementException.class)
public void whenCreateEmptyOptional_thenNull(){
User user = new User("John.gmail.com","1234");
Optional<User> opt = Optional.ofNullable(user);
assertTrue(opt.isPresent());
assertEquals(user.getEmail(),opt.get().getEmail());
}
}

使用ifPresent()方法检查是否有值,该方法除了执行检查,还接收一个Consumer(消费者)参数,如果对象不是空的,就对执行传入的Lambda表达式

1
2
3
4
5
6
7
8
public class TestOptional{
@Test(expected = NoSuchElementException.class)
public void whenCreateEmptyOptional_thenNull(){
User user = new User("John.gmail.com","1234");
Optional<User> opt = Optional.ofNullable(user);
opt.isPresent(u->assertEquals(user.getEmail(),opt.get().getEmail()));
}
}

4. 返回默认值

Optional类提供了API用以返回对象值,或者在对象为空的时候返回默认值
orElse():如果有值则返回该值,否则返回传递给它的参数值,如果初始值不是null则忽略默认值

1
2
3
4
5
6
7
8
public class TestOptional{
@Test(expected = NoSuchElementException.class)
public void whenCreateEmptyOptional_thenNull(){
User user = null;
User user2 = new User("anna@gmail.com", "1234");
User result = Optional.ofNullable(user).orElse(user2);
}
}

orElseGet():有值的时候返回值,没有值的时候执行作为参数传入的Supplier(供应者)函数式接口并将返回其执行结果

1
2
3
4
5
6
7
8
public class TestOptional{
@Test(expected = NoSuchElementException.class)
public void whenCreateEmptyOptional_thenNull(){
User user = null;
User user2 = new User("anna@gmail.com", "1234");
User result = Optional.ofNullable(user).orElseGet( () -> user2);
}
}

orElse()和orElseGet()的的区别

当对象为空而返回默认对象时行为并无差异
当对象不为空时orElse()仍然会创建默认值对象,而orElseGet()方法不创建默认对象。
在执行较密集的调用时,比如调用 Web 服务或数据查询,这个差异会对性能产生重大影响。

5. 返回异常

除了 orElse() 和 orElseGet() 方法,Optional 还定义了 orElseThrow() API,它会在对象为空的时候抛出异常,而不是返回备选的值

1
2
3
4
5
6
public class TestOptional{
@Test(expected = NoSuchElementException.class)
public void whenCreateEmptyOptional_thenNull(){
User result = Optional.ofNullable(user).orElseThrow( () -> new IllegalArgumentException());
}
}

如果 user 值为 null,会抛出 IllegalArgumentException。

6. 转换值

map():对值应用(调用)作为参数的函数,然后将返回的值包装在Optional中

1
2
3
4
5
6
7
public class TestOptional{
@Test(expected = NoSuchElementException.class)
public void whenCreateEmptyOptional_thenNull(){
User user = new User("aaa.163.com","1234");
String email = Optional.ofNullable(user).map(u->u.getEmail).orElse("1234@163.com");
}
}

flatMap():需要函数作为参数,并对值调用这个函数,然后直接返回结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class User {
private String position;

public Optional<String> getPosition() {
return Optional.ofNullable(position);
}
}
public class TestOptional{
@Test(expected = NoSuchElementException.class)
public void whenCreateEmptyOptional_thenNull(){
User user = new User("anna@gmail.com", "1234");
user.setPosition("Developer");
String position = Optional.ofNullable(user)
.flatMap(u -> u.getPosition()).orElse("default");
assertEquals(position, user.getPosition().get());
}
}

getter 方法返回 String 值的 Optional,你可以在对 User 的 Optional 对象调用 flatMap() 时,用它作为参数。其返回的值是解除包装的 String 值

7. 过滤值

filter():接受一个 Predicate 参数,返回测试结果为 true 的值。如果测试结果为 false,会返回一个空的 Optional。

1
2
3
4
5
6
7
8
9
public class TestOptional{
@Test(expected = NoSuchElementException.class)
public void whenCreateEmptyOptional_thenNull(){
User user = new User("anna@gmail.com", "1234");
Optional<User> result = Optional.ofNullable(user)
.filter(u -> u.getEmail() != null && u.getEmail().contains("@"));
assertTrue(result.isPresent());
}
}

8. Java 9增强

Java 9为Optional类增加了三个方法,or(),ifPresentOrElse()和stream()
or() 方法与 orElse() 和 orElseGet() 类似,它们都在对象为空的时候提供了替代情况。or() 的返回值是由 Supplier 参数产生的另一个 Optional 对象。如果对象包含值,则 Lambda 表达式不会执行

1
2
3
4
5
6
7
8
public class TestOptional{
@Test(expected = NoSuchElementException.class)
public void whenCreateEmptyOptional_thenNull(){
User result = Optional.ofNullable(user)
.or( () -> Optional.of(new User("default","1234"))).get();
assertEquals(result.getEmail(), "default");
}
}

ifPresentOrElse()方法需要两个参数:一个 Consumer 和一个 Runnable。如果对象包含值,会执行 Consumer 的动作,否则运行 Runnable。
如果你想在有值的时候执行某个动作,或者只是跟踪是否定义了某个值,那么这个方法非常有用:

1
2
3
4
5
6
7
public class TestOptional{
@Test(expected = NoSuchElementException.class)
public void whenCreateEmptyOptional_thenNull(){
Optional.ofNullable(user).ifPresentOrElse( u -> logger.info("User is:" + u.getEmail()),
() -> logger.info("User not found"));
}
}

stream()把实例转换为 Stream 对象,如果没有值,它会得到空的 Stream;有值的情况下,Stream 则会包含单一值。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class TestOptional{
@Test(expected = NoSuchElementException.class)
public void whenCreateEmptyOptional_thenNull(){
User user = new User("john@gmail.com", "1234");
List<String> emails = Optional.ofNullable(user)
.stream()
.filter(u -> u.getEmail() != null && u.getEmail().contains("@"))
.map( u -> u.getEmail())
.collect(Collectors.toList());
assertTrue(emails.size() == 1);
assertEquals(emails.get(0), user.getEmail());
}
}
  • 标题: Java8之Optional
  • 作者: zlong.w
  • 创建于 : 2023-10-20 17:30:56
  • 更新于 : 2023-10-23 15:34:55
  • 链接: https://zlonx.cn/2023/10/20/Java8之Optional/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
此页目录
Java8之Optional