当前位置: 首页 > news >正文

Java Optional:优雅处理空值的艺术,告别NullPointerException

在这里插入图片描述

目录

    • 一、Optional 是什么?
    • 二、为什么要用 Optional?
    • 三、Optional 的使用方法
    • 四、实际代码示例:处理用户信息的场景
    • 五、总结

🌟我的其他文章也讲解的比较有趣😁,如果喜欢博主的讲解方式,可以多多支持一下,感谢🤗!

🌟了解 SpringBoot 自动装配原理请看: SpringBoot自动装配原理详解!结合源代码讲解!

其他优质专栏: 【🎇SpringBoot】【🎉多线程】【🎨Redis】【✨设计模式专栏(已完结)】…等

如果喜欢作者的讲解方式,可以点赞收藏加关注,你的支持就是我的动力
✨更多文章请看个人主页: 码熔burning

一、Optional 是什么?

想象一下,你有一个盒子📦,这个盒子里面可能装着一个礼物🎁(一个值),也可能什么都没有(空值)。Optional就像这个盒子,它用来包装一个值,但这个值可能存在,也可能不存在。

二、为什么要用 Optional?

在Java中,我们经常会遇到null值。null值本身没什么问题,但如果我们不小心使用了null值,就可能会抛出NullPointerException(空指针异常💥),这是一种非常常见的bug。

Optional 的出现就是为了解决这个问题。它强迫你显式地处理值可能为空的情况,从而减少NullPointerException的发生,让你的代码更健壮💪、更易读📖。

三、Optional 的使用方法

  1. 创建 Optional 对象
  • Optional.of(value) 如果 value 确定不是 null,可以使用这个方法。如果 valuenull,会立即抛出 NullPointerException

    String name = "Alice";
    Optional<String> optionalName = Optional.of(name); // 创建一个包含 "Alice" 的 Optional
    
  • Optional.ofNullable(value) 如果 value 可能为 null,应该使用这个方法。如果 valuenull,会创建一个空的 Optional

    String address = null; // 假设 address 可能为 null
    Optional<String> optionalAddress = Optional.ofNullable(address); // 创建一个空的 Optional
    
  • Optional.empty() 创建一个空的 Optional

    Optional<Integer> emptyOptional = Optional.empty(); // 创建一个空的 Optional
    
  1. 检查 Optional 是否包含值
  • isPresent() 如果 Optional 包含值,返回 true✅;否则返回 false❌。

    Optional<String> optionalName = Optional.of("Bob");
    if (optionalName.isPresent()) {
        System.out.println("Name is present!");
    } else {
        System.out.println("Name is not present!");
    }
    
  1. 获取 Optional 中的值
  • get() 如果 Optional 包含值,返回该值。如果 Optional 为空,会抛出 NoSuchElementException注意⚠️: 除非你确定 Optional 包含值,否则不要直接使用 get(),因为它可能会抛出异常。

    Optional<String> optionalCity = Optional.of("New York");
    if (optionalCity.isPresent()) {
        String city = optionalCity.get(); // 获取值
        System.out.println("City: " + city);
    }
    
  • orElse(defaultValue) 如果 Optional 包含值,返回该值;否则返回 defaultValue。这是最常用的安全获取值的方式。

    Optional<String> optionalCountry = Optional.ofNullable(null); // 假设 country 可能为 null
    String country = optionalCountry.orElse("Unknown"); // 如果为空,返回 "Unknown"
    System.out.println("Country: " + country); // 输出 "Country: Unknown"
    
  • orElseGet(supplier) 如果 Optional 包含值,返回该值;否则调用 supplier 函数生成一个默认值并返回。 supplier 是一个函数式接口,它不接受任何参数,但返回一个值。 这个方法适用于默认值的计算比较耗时的情况,因为只有在 Optional 为空时才会调用 supplier

    Optional<Integer> optionalAge = Optional.empty();
    int age = optionalAge.orElseGet(() -> {
        // 模拟耗时操作
        System.out.println("Calculating default age...");
        return 18;
    });
    System.out.println("Age: " + age);
    
  • orElseThrow(exceptionSupplier) 如果 Optional 包含值,返回该值;否则调用 exceptionSupplier 函数生成一个异常并抛出。 exceptionSupplier 是一个函数式接口,它不接受任何参数,但返回一个 Throwable 类型的异常。

    Optional<String> optionalEmail = Optional.empty();
    String email = optionalEmail.orElseThrow(() -> new IllegalArgumentException("Email is required"));
    // 如果 email 为空,会抛出 IllegalArgumentException
    
  1. 使用 Optional 进行转换和过滤
  • map(function) 如果 Optional 包含值,则将该值传递给 function 函数进行转换,并将转换后的结果包装成一个新的 Optional 返回。如果 Optional 为空,则直接返回一个空的 Optional

    Optional<String> optionalName = Optional.of("Charlie");
    Optional<Integer> optionalNameLength = optionalName.map(String::length); // 获取名字的长度
    System.out.println("Name length: " + optionalNameLength.orElse(0)); // 输出 "Name length: 7"
    
  • flatMap(function)map() 类似,但 function 函数的返回值必须是一个 OptionalflatMap() 会将 function 返回的 Optional 直接返回,而不会再次包装。 这通常用于处理嵌套的 Optional

    class Address {
        private Optional<String> street;
    
        public Address(Optional<String> street) {
            this.street = street;
        }
    
        public Optional<String> getStreet() {
            return street;
        }
    }
    
    class Person {
        private Optional<Address> address;
    
        public Person(Optional<Address> address) {
            this.address = address;
        }
    
        public Optional<Address> getAddress() {
            return address;
        }
    }
    
    Optional<Person> person = Optional.of(new Person(Optional.of(new Address(Optional.of("Main Street")))));
    
    // 使用 flatMap 获取街道名称
    Optional<String> streetName = person.flatMap(Person::getAddress)
                                        .flatMap(Address::getStreet);
    
    System.out.println("Street name: " + streetName.orElse("Unknown")); // 输出 "Street name: Main Street"
    
  • filter(predicate) 如果 Optional 包含值,并且该值满足 predicate 条件,则返回包含该值的 Optional;否则返回一个空的 Optionalpredicate 是一个函数式接口,它接受一个参数并返回一个布尔值。

    Optional<Integer> optionalAge = Optional.of(25);
    Optional<Integer> adultAge = optionalAge.filter(age -> age >= 18); // 过滤出成年人的年龄
    if (adultAge.isPresent()) {
        System.out.println("Adult age: " + adultAge.get()); // 输出 "Adult age: 25"
    }
    

四、实际代码示例:处理用户信息的场景

假设我们有一个 UserService 类,用于获取用户信息。用户信息中可能包含地址信息,地址信息中可能包含街道信息。

import java.util.Optional;

class Address {
    private String street;

    public Address(String street) {
        this.street = street;
    }

    public Optional<String> getStreet() {
        return Optional.ofNullable(street); // 使用 Optional 包装 street
    }
}

class User {
    private String name;
    private Address address;

    public User(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public Optional<Address> getAddress() {
        return Optional.ofNullable(address); // 使用 Optional 包装 address
    }
}

class UserService {
    public Optional<User> getUserById(int id) {
        // 模拟从数据库获取用户信息
        if (id == 1) {
            return Optional.of(new User("David", new Address("Park Avenue")));
        } else if (id == 2) {
            return Optional.of(new User("Eve", null)); // Eve 没有地址
        } else {
            return Optional.empty(); // 没有找到用户
        }
    }
}

public class OptionalExample {
    public static void main(String[] args) {
        UserService userService = new UserService();

        // 获取用户 ID 为 1 的用户的街道名称
        Optional<User> user1 = userService.getUserById(1);
        String street1 = user1.flatMap(User::getAddress)
                             .flatMap(Address::getStreet)
                             .orElse("Unknown");
        System.out.println("User 1 street: " + street1); // 输出 "User 1 street: Park Avenue"

        // 获取用户 ID 为 2 的用户的街道名称
        Optional<User> user2 = userService.getUserById(2);
        String street2 = user2.flatMap(User::getAddress)
                             .flatMap(Address::getStreet)
                             .orElse("Unknown");
        System.out.println("User 2 street: " + street2); // 输出 "User 2 street: Unknown"

        // 获取用户 ID 为 3 的用户的街道名称
        Optional<User> user3 = userService.getUserById(3);
        String street3 = user3.flatMap(User::getAddress)
                             .flatMap(Address::getStreet)
                             .orElse("Unknown");
        System.out.println("User 3 street: " + street3); // 输出 "User 3 street: Unknown"
    }
}

五、总结

  • Optional 是一个容器,用于包装可能为空的值。📦
  • 使用 Optional 可以避免 NullPointerException。💥
  • Optional 提供了多种方法来安全地获取值、进行转换和过滤。🛡️
  • 在可能出现 null 值的地方,考虑使用 Optional 来提高代码的健壮性和可读性。💪📖

最佳实践:

  • 不要过度使用 Optional Optional 主要用于返回值,表示某个值可能不存在。 不要将 Optional 作为类的字段,或者方法的参数,这会增加代码的复杂性。
  • 避免使用 Optional.get() 除非你确定 Optional 包含值,否则不要直接使用 get(),因为它可能会抛出异常。 使用 orElse(), orElseGet(), 或 orElseThrow() 来安全地获取值。
  • 使用 map()flatMap() 进行链式操作。 map()flatMap() 可以让你以一种流畅的方式对 Optional 中的值进行转换和处理。✨

希望这篇文章能够帮助你理解和使用 Java 中的 Optional。 记住,Optional 是一种工具🛠️,应该在合适的地方使用,以提高代码的质量。💯

http://www.dtcms.com/a/97005.html

相关文章:

  • 高并发金融系统,“可观测-可追溯-可回滚“的闭环审计体系
  • 小林coding-17道Java基础面试题
  • MySQL 基础查询语句参考手册
  • 【Zabbix技术系列文章】第①篇——基础入门
  • CSS学习笔记5——渐变属性+盒子模型阶段案例
  • ubuntu 升级补丁,备份备份备份
  • JAVA学习*异常
  • CSS-BFC(块级格式化上下文)
  • 主流大模型采用的架构、注意力机制、位置编码等汇总表
  • 【SECS】初识SECS协议
  • MiniRAG检索流程详细图解
  • #VCS# 关于 +incdir+xxx 编译选项的注意点
  • #前端js发异步请求的几种方式
  • 【AI语音】edge-tts实现文本转语音,免费且音质不错
  • 指针 --1
  • RS232转Profinet网关技术,检漏仪新篇章!
  • 深度融合华为鸿蒙生态,嘀嗒出行重构顺风车出行新体验
  • 「HTML5+Canvas实战」星际空战游戏开发 - 纯前端实现 源码即开即用【附演示视频】
  • 18-背景渐变与阴影(CSS3)
  • C++ | constexpr
  • Linux服务器怎样根据端口找到对应启动的服务
  • TCSVT审稿学习笔记
  • 3.28-2 jmeter读取mysql
  • spring @SpringBootApplication 注解详解
  • 使用AURIX ADS部署tensorflow lite到Tricore TC2XX/TC3XX
  • EMC知识学习三
  • ecovadis评估有什么流程?对企业发展的重要意义
  • HTML应用指南:利用GET请求获取全国无印良品门店位置信息
  • 19726 星际旅行
  • 【SDMs分析1】基于ENMTools R包的生态位分化分析和图像绘制(identity.test())