目录
前言
学习了b站up主AlbertShen的视频,受益匪浅,写下这篇笔记,地址:Java-Optional视频
一、Optional 的用途
在 Java 编程中,null值常常带来 NullPointerException 的风险,使得代码充斥着冗长的 null 检查语句,降低了代码可读性和可维护性。为了解决这个问题,Java 8 引入了 Optional 类,其主要用途如下:
-
解决空指针异常: Optional 类就像一个容器,可以包含值或为空。通过提供一系列操作方法,可以方便地操作内部的值,避免直接访问可能为空的对象,从而有效防止空指针异常。
-
提高代码可读性: 使用 Optional 可以清晰地表达返回值可能为空的情况,使代码更加易于理解和维护。
-
支持函数式编程: Optional 的设计也考虑了函数式编程的原则,可以与 Lambda 表达式和 Stream API 等特性结合使用,实现更优雅的链式调用。
二、Optional 的创建
Optional.empty(): 创建一个空的 Optional 对象,表示没有值。
Optional<Object> optionalBox = Optional.empty();
System.out.println(optionalBox.isPresent()); // 输出: false
System.out.println(optionalBox.isEmpty()); // 输出: true
Optional.of(value): 创建一个包含非空值的 Optional 对象。如果传入的值为 null,则会抛出 NullPointerException。
String value = "maike";
Optional<String> optionalBox = Optional.of(value);
System.out.println(optionalBox.isPresent()); // 输出: true
System.out.println(optionalBox.isEmpty()); // 输出: false
Optional.ofNullable(value): 创建一个 Optional 对象,可以包含值或为空,适用于处理不确定是否为空的情况。
String value = null;
Optional<String> optionalBox = Optional.ofNullable(value);
System.out.println(optionalBox.isPresent()); // 输出: false
System.out.println(optionalBox.isEmpty()); // 输出: true
三、Optional 的常用方法
-
isPresent(): 检查 Optional 对象内部是否存在值,如果存在则返回 true,否则返回 false。
-
isEmpty(): 与 isPresent() 相反,如果 Optional 对象为空则返回 true,否则返回 false。
-
get(): 获取 Optional 对象内部的值。如果 Optional 对象为空,则抛出 NoSuchElementException。
不推荐直接使用 get() 方法获取值,因为它没有进行空值检查,可能会导致异常。
-
orElse(other): 如果 Optional 对象包含值,则返回该值,否则返回指定的默认值 other。
-
Optional<String> optionalName = Optional.empty(); String name = optionalName.orElse("Default Name"); System.out.println(name); // 输出: Default Name
- orElseGet(supplier): 如果 Optional 对象包含值,则返回该值,否则调用 supplier 提供的函数生成一个默认值。 orElseGet() 方法只会在 Optional 对象为空时才执行 supplier 函数,效率更高。
-
Optional<User> optionalUser = userRepository.findUserByName("NonExistingUser"); User user = optionalUser.orElseGet(() -> new User("Default", "User")); System.out.println(user.getFullName()); // 输出: Default User
- orElseThrow(supplier): 如果 Optional 对象包含值,则返回该值,否则抛出 supplier 提供的异常。
-
Optional<User> optionalUser = userRepository.findUserByName("NonExistingUser"); User user = optionalUser.orElseThrow(() -> new RuntimeException("User not found"));
-
orElseThrow(supplier): 如果 Optional 对象包含值,则返回该值,否则抛出 supplier 提供的异常。
-
Optional<User> optionalUser = userRepository.findUserByName("NonExistingUser"); User user = optionalUser.orElseThrow(() -> new RuntimeException("User not found"));
-
filter(predicate): 根据指定的条件过滤 Optional 对象中的值。如果值存在且满足条件,则返回包含原始值的新的 Optional 对象;否则返回空的 Optional 对象。
-
filter(predicate): 根据指定的条件过滤 Optional 对象中的值。如果值存在且满足条件,则返回包含原始值的新的 Optional 对象;否则返回空的 Optional 对象。
-
map(function): 对 Optional 对象中的值进行转换,并返回一个包含转换后值的新的 Optional 对象。如果 Optional 对象为空,则 map 方法什么也不做,直接返回一个空的 Optional 对象。
-
Optional<User> optionalUser = userRepository.findUserByName("Albert"); Optional<String> optionalName = optionalUser.map(User::getName); System.out.println(optionalName.orElse("Unknown")); // 输出: Albert
-
flatMap(function): 用于扁平化嵌套的 Optional 结构,避免引入不必要的嵌套层级。flatMap 的转换函数返回的必须是另一个 Optional 对象,如果原始的 Optional 对象为空,或者转换函数返回的 Optional 对象为空,则最终得到的也是一个空的 Optional 对象。
-
Optional<User> optionalUser = userRepository.findUserByName("Albert"); Optional<String> optionalCity = optionalUser.flatMap(u -> u.getAddress().flatMap(Address::getCity)); System.out.println(optionalCity.orElse("Unknown City"));
-
stream(): 将 Optional 对象转换为 Stream 对象,方便与 Stream API 进行集成。
-
Optional<User> optionalUser = userRepository.findUserByName("Albert"); optionalUser.stream().forEach(u -> System.out.println(u.getFullName()));
四、Optional优化案例
-
传统方法
-
public class UserRepository { public User findUserByName(String name) { // ... (数据库查询逻辑) if (/* 查询结果不为空 */) { return new User( /* 查询结果 */ ); } else { return null; } } } public class Main { public static void main(String[] args) { UserRepository userRepository = new UserRepository(); User user = userRepository.findUserByName("Albert2"); if (user != null) { System.out.println(user.getFullName()); } else { User defaultUser = new User("Neo", "Thomas Anderson"); System.out.println(defaultUser.getFullName()); } } }
findUserByName() 方法返回 User 对象或 null,需要调用者进行显式 null 检
-
Optional 优化方法:
-
import java.util.Optional; public class UserRepository { public Optional<User> findUserByName(String name) { // ... (数据库查询逻辑) if (/* 查询结果不为空 */) { return Optional.of(new User( /* 查询结果 */ )); } else { return Optional.empty(); } } } public class Main { public static void main(String[] args) { UserRepository userRepository = new UserRepository(); Optional<User> optionalUser = userRepository.findUserByName("Albert2"); // 使用 orElse() 提供默认用户 System.out.println(optionalUser.orElse(new User("Neo", "Thomas Anderson")).getFullName()); // 使用 ifPresent() 处理存在值的情况 optionalUser.ifPresent(user -> System.out.println(user.getFullName())); // 使用 orElseThrow() 抛出自定义异常 User user = optionalUser.orElseThrow(() -> new RuntimeException("User not found")); } }
五、Optional的使用建议
-
使用Optional作为方法的返回值类型,表示方法可能不返回结果。
-
不推荐使用Optional作为类的字段,因为会增加内存消耗,并且会使对象的序列化变得复杂。
-
不推荐使用Optional作为构造器参数,因为会强制调用者创建Optional实例,应该通过构造器重载来解决。
-
不应该用作集合的参数类型,集合本身已经可以很好地处理空集合的情况,没必要用Optional包装集合。
-
不建议使用get()方法,调用Optional的get()方法前没有确认值是否存在,可能会导致NoSuchElementException。
-
应当使用ifPresent()、orElse()、orElseGet()、orElseThrow()等方法,根据Optional对象是否包含值来执行不同的操作。
六、总结
Optional 类是 Java 提供的一个强大的工具,可以帮助我们更好地处理可能为空的值,避免 NullPointerException,提高代码质量。