【Java】Optional类入门使用

认识

概述

Java 8 引入的 Optional 类,是一个可以包含可能为空的值的容器对象。它的目的是为了提供一种更优雅的方式来处理可能为 null 的值,从而避免直接使用 null 值可能引发的 NullPointerException。

使用 Optional 类可以显著改善代码的可读性和健壮性。

特点

Optional 类的主要特点:

防止 NullPointerException:使用 Optional 可以明确地要求用户在使用变量之前处理 null 情况;
增强代码可读性:通过使用 Optional 的方法,代码的可读性和意图更加明确;
集成到 Java 的流 API 中:Optional 类型在 Java 8 的流(Stream)操作中被广泛使用,提供了更复杂的条件查询和变换功能。

Optional的使用

在这个例子中,selectById 方法的返回值被包装在一个 Optional 中。这使得可以直接使用 map 方法来转换 UserDO 到 UserVO,而不需要手动检查 null。如果 userDO 为 null,map 方法不会执行其内部的 lambda 表达式,并且 getUserVOById 方法将返回 orElse 方法中所指定的默认值。

  • 不使用Optional
public UserVO getUserVOById(Long userId) {
    UserDO userDO = userMapper.selectById(userId);  // MyBatis 的查询
    if (userDO == null) {
        return null;
    }
    UserVO userVO = new UserVO();
    userVO.setName(userDO.getName());
    userVO.setEmail(userDO.getEmail());
    // 更多字段赋值...
    return userVO;
}

  • 使用Optional
public UserVO getUserVOById(Long userId) {
    Optional<UserDO> userDOOptional = Optional.ofNullable(userMapper.selectById(userId));
    return userDOOptional.map(userDO -> {
        UserVO userVO = new UserVO();
        userVO.setName(userDO.getName());
        userVO.setEmail(userDO.getEmail());
        // 更多字段赋值...
        return userVO;
    // 如果有值则返回该值,否则返回 null
    }).orElse(null);
}

Java8中Optional类的主要方法

1.创建Optional对象

Optional.of(T value) 方法

创建一个包含非空值的 Optional 对象。如果传入的 value 是 null,则会立即抛出 NullPointerException。这个方法用于包装那些确定不为 null 的值。

Optional<String> opt = Optional.of("Hello");
Optional.empty() 方法

创建一个空的 Optional 实例。用于表示一个可能存在或不存在的值

其实就是不想让你用null值,这个还要根Optional类的其他API搭配使用,Optional 提供的 API(如 isPresent()ifPresent()orElse() 等)

public class MyTest {
    public static void main(String[] args) {
//        Optional<String> name = getNameById(1);
        Optional<String> name = getNameById(0);
        System.out.println(name);
        // 使用isPresent() 或者 ifPresent() 方法来检查是否存在值
        name.ifPresent(new Consumer<String>() {
            @Override
            public void accept(String n) {
                // 如果为空值,可以给出默认值
                System.out.println("name is " + name.orElse("黄小桃"));
            }
        });

        // 与集合结合使用
        List<String> names = Arrays.asList("黄小桃", "王心妍", "霍雨蝶");
        names.forEach(new Consumer<String>() {
            @Override
            public void accept(String name) {
                System.out.println(name);
            }
        });
    }

    public static Optional<String> getNameById(int id){
        if (id == 1){
            return Optional.of("AutismBtkrsr");
        }else {
            return Optional.empty(); // 返回一个空的Optional
        }
    }
}



再补一个例子

    /**
     * consumer接口测试
     */
    @Test
    public void test_Consumer() {
        //① 使用consumer接口实现方法
        Consumer<String> consumer = new Consumer<String>() {

            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        Stream<String> stream = Stream.of("aaa", "bbb", "ddd", "ccc", "fff");
        stream.forEach(consumer);

        System.out.println("********************");

        //② 使用lambda表达式,forEach方法需要的就是一个Consumer接口
        stream = Stream.of("aaa", "bbb", "ddd", "ccc", "fff");
        Consumer<String> consumer1 = (s) -> System.out.println(s);//lambda表达式返回的就是一个Consumer接口
        stream.forEach(consumer1);
        //更直接的方式
        //stream.forEach((s) -> System.out.println(s));
        System.out.println("********************");

        //③ 使用方法引用,方法引用也是一个consumer
        stream = Stream.of("aaa", "bbb", "ddd", "ccc", "fff");
        Consumer consumer2 = System.out::println;
        stream.forEach(consumer);
        //更直接的方式
        //stream.forEach(System.out::println);
    }
Optional.ofNullable(T value) 方法

根据传入的 value 创建一个 Optional 对象。

如果 value 为 null,则返回一个空的 Optional 实例;如果 value 非 null,则创建一个包含该值的 Optional 对象。这个方法用于可能为 null 的情况,提供了一种安全的方式来包装 null 值。

Optional<String> opt = Optional.ofNullable(null);

2.条件API

isPresent() 方法

检查 Optional 是否包含值。如果包含值,则返回 true;否则返回 false

这个方法通常在需要基于 Optional 中是否有值来执行不同操作的场合使用。

Optional<String> opt = Optional.of("Hello");
if (opt.isPresent()) {
    System.out.println("Value is present.");
} else {
    System.out.println("Value is not present.");
}
ifPresent(Consumer<? super T> consumer) 方法

如果 Optional 对象包含值,则执行给定的 consumer 操作。这个方法常用于在值存在的情况下执行某些操作,而无需进行显式的空检查。

Optional<String> opt = Optional.of("Hello");
opt.ifPresent(name -> System.out.println("Name is " + name));

3.值获取

orElse(T other) 方法

如果有值则返回该值,否则返回 other。这个方法用于处理 Optional 对象可能为空的情况,提供一个默认值。

String name = Optional.ofNullable(null).orElse("Default Name");
System.out.println(name);  // 输出 "Default Name"
get() 方法

如果 Optional 对象包含值,则返回此值;否则抛出 NoSuchElementException。使用 get() 方法前应该先检查是否有值(例如使用 isPresent()),或者使用捕获异常的方式来处理可能的 NoSuchElementException

Optional<String> opt = Optional.of("Hello");
if (opt.isPresent()) {
    System.out.println(opt.get());  // 输出 "Hello"
} else {
    System.out.println("No value present");
}
orElseGet(Supplier<? extends T> supplier) 方法

如果有值则返回该值,否则执行 supplier 提供的备选的生成策略,返回生成的值。这个方法通常用于延迟计算默认值或获取值的成本较高时。

String name = Optional.ofNullable(null).orElseGet(() -> {
    // Perform some expensive operation or default value computation
    return "Computed Default Name";
});
System.out.println(name);  // 输出 "Computed Default Name"
orElseThrow(Supplier<? extends X> exceptionSupplier) 方法

如果 Optional 对象包含值,则返回此值;如果没有值,抛出由 exceptionSupplier 提供的异常。这使得用户可以定义缺失值时抛出的异常类型。

String result = Optional.ofNullable(null).orElseThrow(() -> new IllegalStateException("Value not present"));
// 将抛出 IllegalStateException

通过以上方法,Optional 提供了灵活的机制来从可能为空的对象中安全地获取值

4.转换和过滤


map(Function<? super T, ? extends U> mapper) 方法


如果有值,应用提供的映射函数 mapper,并返回一个 Optional 对象来包含映射函数的结果。如果原 Optional 为空,则仍返回一个空的 Optional。

Optional<String> opt = Optional.of("hello");
Optional<String> upper = opt.map(String::toUpperCase);
upper.ifPresent(System.out::println);  // 输出 "HELLO"



通过这些方法,Optional 类为 Java 开发者提供了一种强大的工具来处理可为空的情况,增强了程序的健壮性和可读性。

flatMap(Function<? super T, Optional<U>> mapper) 方法


如果 Optional 对象有值,对其值应用提供的 mapper 函数,该函数必须返回 Optional 类型的结果。此方法用于避免嵌套 Optional(即 Optional<Optional<T>>)的情况,使结果保持在单一层级的 Optional。

Optional<String> optionalString = Optional.of("hello");
Optional<Integer> length = optionalString.flatMap(s -> Optional.of(s.length()));
length.ifPresent(System.out::println);  // 输出 5


filter(Predicate<? super T> predicate) 方法


如果 Optional 对象有值,并且该值满足提供的 predicate 条件,则返回包含该值的 Optional;如果不满足条件,则返回一个空的 Optional。这使得 Optional 可以集成更复杂的条件逻辑,提供类似流中的过滤功能。

Optional<String> optionalString = Optional.of("hello");
Optional<String> longString = optionalString.filter(s -> s.length() > 3);
longString.ifPresent(System.out::println);  // 输出 "hello"

Optional<String> shortString = optionalString.filter(s -> s.length() < 3);
shortString.ifPresent(System.out::println);  // 不输出任何内容

Optional 类使用时的注意事项


使用 Optional 类时,尽管它提供了很多便利,但也有一些注意事项和最佳实践需要遵循,以确保代码的健康性和性能。以下是使用 Optional 时的主要注意事项:

不要在类的字段中使用 Optional


使用 Optional 作为类的字段通常不推荐,因为 Optional 旨在作为方法的返回类型,用于有效地表示可空的结果。使用它作为字段类型会增加内存开销,同时也违反了其用作临时包装器的设计初衷。

避免使用 Optional 作为参数


将 Optional 用作方法参数通常是多余的。这种做法迫使调用者使用 Optional,而不是允许他们以更自然的方式传递值或 null。更好的方法是允许传递 null 并在方法内部使用 Optional.ofNullable() 进行处理。

不要仅为了避免 null 检查而使用 Optional


Optional 的过度使用可能导致代码质量下降,特别是当它被用来包装几乎从不为 null 的值时。它应该用在真正可能为空的情况,否则会导致不必要的复杂性。

使用 Optional 的链式调用


利用 Optional 的链式调用,如 map、flatMap、filter 等,可以编写出更简洁、更声明式的代码。这种方式有助于提高代码的可读性和维护性。

避免在 Optional 上进行显式的 null 检查


Optional 的目的是为了消除代码中的 null 检查。对 Optional 对象本身进行 null 检查是没有必要的,也违背了使用 Optional 的初衷。

在使用 get() 方法之前总是检查是否有值


直接调用 Optional.get() 而不先检查是否有值,可能会引发 NoSuchElementException。应该使用 isPresent() 或更好的方法是 orElse(), orElseGet(), 或 orElseThrow() 等,以更安全的方式访问值。

利用 Optional 提供的方法避免抛出异常


在可能的情况下,优先使用 orElse() 或 orElseGet() 而不是 orElseThrow(),除非你确实需要在没有值的情况下抛出一个异常。这样做可以使得程序的控制流更加清晰。

理解 Optional 和性能的关系


虽然 Optional 在编码安全性上提供了很多好处,但它也有一定的性能开销,因为每次使用 Optional 都涉及到创建新的对象实例。在性能敏感的应用中,滥用 Optional 可能会导致问题。

总之,Optional 是一个非常有用的工具,能够帮助Java开发者写出更清洁、更健壮的代码。然而,像任何工具一样,它需要在适当的情况下正确使用。理解何时以及如何使用 Optional,可以避免许多常见的编程错误,并最大化其优势。

orElse方法的使用


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

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


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

Optional的序列化问题


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值