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

Optional类

0.由来

实际 Java 开发过程中,尝试访问空引用的属性或者调用空引用的方法,会报 空指针异常NullPointerException)。处理可能为 null 的值时,需要增加很多 条件判定,比如:

💗User:

@Data
@AllArgsConstructor
public class User {String name;String fullName;
}

💙UserRepository:

public class UserRepository {public User findUserByName(String name){if(name.equals("Shwen")){return new User("Shwen","Shwen shen");} else {return null;}}
}

👉OptionalTest:

public class OptionalTest {public static void main(String[] args) {UserRepository userRepository = new UserRepository();User user = userRepository.findUserByName("Shwen");if(user!=null){System.out.println(user.getFullName());} else {User defaultUser = new User("Stark", "Tony Stark");System.out.println(defaultUser.getFullName());}}
}

代码会有很多的条件判断,难以阅读与维护。

1.概述

Optional 类引入了一种显式的方式来处理可能为空的对象,强制程序员在可能为空的情况下进行显式的处理,以避免空指针异常。

Optional 是一个容器对象,用于封装可能为 null 的值。当一个方法声明返回 Optional<T> 时,它将明确地表示该方法可能会返回 null。这种设计模式有助于减少空指针异常的风险,并鼓励更好的编程实践。

Optional 类提供了一系列方法来方便地操作内部的值。常用的方法有getorElseorElseGetorElseThrow等。

Optional 的设计也考虑了 函数式编程 的原则,可以与 Lambda 表达式和 StreamAPI 等特性结合使用,可以进行链式调用替代命令式编程的方式(通过编写if条件语句检查null值)。

2.Optional对象的方法

2.1 创建Optional实例

  • of:创建不为 null 的对象,若 value 为 null,则抛出 NullPointerException 异常。
Optional.of(new User("Shwen","Shwen shen"));
  • ofNullable:创建可能为 null 的对象
Optional.ofNullable(new User("Shwen","Shwen shen"));
  • empty:创建的对象为 null
Optional.empty();

💙改造UserRepository:

public class UserRepository {public Optional<User> findUserByName(String name){if(name.equals("Shwen")){return Optional.ofNullable(new User("Shwen","Shwen shen"));} else {return Optional.empty();}}
}

2.2 判断方法

  • isPresent:用于检查 optional 内是否存在值,返回为 boolean,存在为 true,不存在为 false
boolean flag = optionalUser.isPresent();

2.3 获取方法

  • get:若取值对象为 null,会报 NoSuchElementException 异常(并非 NullPointerException 异常)。
User user = optionalUser.get();
  • orElse:用于获取值或在值为 null 的情况下提供一个默认值。
public class OptionalTest {public static void main(String[] args) {UserRepository userRepository = new UserRepository();Optional<User> optionalUser = userRepository.findUserByName("Shwen2");// orElse 用于获取值,或在值为 null 的情况下提供一个默认值。User user = optionalUser.orElse(new User("Stark","Tony Stark"));System.out.println(user.getFullName());}
}

optionalUser 有值的情况下,orElse 返回值;值为 null 的情况下,返回默认值 new User("Stark","Tony Stark")

  • orElseGet:功能同 orElse,区别在于方法的参数为 Supplier 的函数式接口,需要使用 Lambda 表达式实现。
User user = optionalUser.orElseGet(() -> new User("Stark","Tony Stark"));

📌orElseorElseGet 区别如下:

  1. orElse 入参是对象,orElseGet入参是 Supplier 的函数式接口
  2. orElse 即使获取的数据不为 null,仍会创建一个新 User 对象。而 orElseGet 不会创建。因此,最好使用只有读取的数据为 null 的时候才会新建对象的 orElseGet 方法。
  • orElseThrow:用于在 Optional 对象中的值为 null 时抛出一个指定的异常。
User user = optionalUser.orElseThrow(() -> new RuntimeException("User not found"));

2.4 判断获取方法

  • ifPresent:参数中若对象不为 null,则会执行 Labmda 中的方法;若参数对象为 null,则不会执行Labmda中的方法,也不会报错
optionalUser.ifPresent(user -> System.out.println(user.getFullName()));
  • ifPresentOrElse:当我们希望值为 null 时进行其他操作,需要使用 ifPresentOrElse 方法。⚠️注:ifPresentOrElse 方法是 Java 9 引入的。
optionalUser.ifPresentOrElse(user -> System.out.println(user.getFullName()),()->System.out.println("User not found"));
  • filter:若满足 filter 方法中的条件,则会返回包含值的 Optional 对象,如果不满足,则返回空的 Optional 对象。
Optional<User> optionalUser2 = optionalUser.filter(user -> user.getFullName().equals("Shwen shen"));
System.out.println(optionalUser2.isPresent());

2.5 map与flatMap方法

2.5.1 map

public<U> Optional<U> map(Function<? super T, ? extends U> mapper)
public class OptionalTest {public static void main(String[] args) {UserRepository userRepository = new UserRepository();Optional<User> optionalUser = userRepository.findUserByName("Shwen2");Optional<String> optional = optionalUser.map(User::getFullName);System.out.println(optional.orElse("User not found"));}
}输出:
User not found

Optional 中的值进行转换,若值为 null,则 map 方法什么也不会做,直接返回空的 Optional 对象。map 方法不会改变原始的 Optional 对象,而返回新的 Optional 对象,因此可以链式调用进行多个转换操作。

2.5.2 flatMap

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

flatMap 方法用于扁平化嵌套的 Optional 结构,以避免引入不必要的嵌套层级,具体为 flatMap 的转换函数返回的必须是另一个 Optional 对象,意味着 flatMap 方法可以用于嵌套的 Optional 情况,可以将两个为嵌套关系的 Optional 对象转换为一个。

如果原始的 Optional 对象为 null,或转换函数返回的 Optional 对象为 null,那么最终得到的也是为 nullOptional 对象。

若只需要对 Optional 对象中的值进行转换,而不需要嵌套的 Optional,那么使用 map 方法更合适。如果要进行一些操作返回另外一个 Optional 对象,flatMap 方法更合适。

2.6 Stream方法

Optionalstream 方法,可以将Optional 对象转换为 Stream 对象,对其中的值进行流操作

如果 Optional 对象包含值,则将这个值封装到一个 Stream 流中,如果 Optional 对象为空,则创造一个为空的 Stream 流。

Stream<String> stream = optionalUser.map(User::getName).stream();
stream.forEach(System.out::println);

⚠️注:stream 方法是 Java 9 引入的。

3.不适合场景

  • 不应该用于类的字段,会增加内存消耗,并使序列化变得复杂;
  • 不应该用于方法参数,使方法的理解和使用变得复杂;
  • 不应用于构造器参数,迫使调用者创建 Optional 实例,应该通过构造器重载解决;
  • 不应该用于集合的参数,集合已经很好的处理空集合的情况,没必要使用 Optional 包装集合;
  • 不建议使用 get 方法,若为 null 会报错。建议使用 orElseGet

4.适用场景

应用场景一般用于方法返回值或者集合操作中。

场景一:
Optional 主要用作返回类型。在获取到这个类型的实例后,如果它有值,你可以取得这个值,否则可以进行一些替代行为。

User user = new User("shwen", null);
String fullName = Optional.ofNullable(user.getFullName()).orElse("未知名称");

场景二:
在对集合进行操作时,某些操作可能返回 null(例如在查找元素时)。使用 Optional 可以更好地处理这些情况,而不是直接返回 null

public void whenEmptyStream_thenReturnDefaultOptional() {List<User> users = new ArrayList<>();User user = users.stream().findFirst().orElse(new User("default", "1234"));assertEquals(user.getEmail(), "default");
}

场景三:
设计 API 时,如果某个方法的返回值可能为空,使用 Optional 可以使 API 更加健壮和易于理解。

public Optional<Product> findProductById(String productId) {// 根据产品ID查找产品,产品可能不存在// 使用 Optional 封装返回值// 返回 Optional.ofNullable(product) 或 Optional.empty()
}

场景四:
当处理多层嵌套的对象时,使用 Optional 可以避免深层次的 null 检查,使代码更加简洁。

public class UserInfo {private String name;private Integer age;private Address address;// 省略构造函数和getter/setter
}public class Address {private String city;// 省略构造函数和getter/setter
}public class OptionalTest {public static void main(String[] args) {UserInfo userInfo = new UserInfo();userInfo.setName("张三");userInfo.setAge(25);userInfo.setAddress(null);String cityName = Optional.ofNullable(userInfo.getAddress()).map(Address::getCity).orElse("未知城市");System.out.println(cityName);}
}输出:
未知城市

本例先通过 Optional.ofNullable 获取 Address 信息,返回类型是 Optional<Address>。再通过 map 方法获取城市名称。由于 address 字段为 null,因此 getCity() 方法返回的 Optional<String> 也是空的。最后,通过 orElse 方法返回默认值 “未知城市”。

5.小结

Optional 提供了一系列方法来判断值是否为空、获取值、通过 Lambda 表达式来处理值等操作,使得代码更加健壮和可读性更好。通过使用 Optional,开发者可以更加清晰地表达值的可能为空的情况,并采取相应的处理措施,有效避免了空指针异常的发生。

⚠️注: Java 9Optional 类添加了三个方法:or()ifPresentOrElse()stream()

最后提一嘴,Optional 虽然可以提高代码的健壮性,但需要注意不要滥用。主要缺点如下:

  • 造成代码冗长,本来一个null值就可以解决
  • 引入多一个对象多一个开销,如果本身追求速度,就不用刻意这样
  • 如果嵌套多层对象还这么使用,增加代码维护难度
  • 对新手不友好

📖Optional实战案例


http://www.mrgr.cn/news/77943.html

相关文章:

  • 未来已来:少儿编程竞赛聚焦物联网,激发创新潜力
  • 力扣面试经典 150(上)
  • GitLab使用示例
  • webStorm安装
  • 分层架构 IM 系统之架构演进
  • FPV整机- 穿越机无人机系统 之系统、产品研发
  • Leetcode 51 N Queens
  • 高频面试题(含笔试高频算法整理)基本总结回顾16
  • pinia的使用
  • 【c++篇】掌握动态内存的奥妙
  • Modern Effective C++ item 15:尽可能的使用constexpr
  • 活着就好20241125
  • 禁用达梦DEM的agent
  • 大数取模 详解
  • 【数据库原理】创建与维护表,DDL数据定义语言
  • Java项目实战II基于SpringBoot的教学资料管理系统(开发文档+数据库+源码)
  • 交叉熵 vs focal loss
  • 探索 Python 任务自动化的新境界:Invoke 库揭秘
  • AJAX请求返回报错NET::ERR_CERT_DATE_INVALID
  • 内网渗透横向移动1
  • Redis设计与实现 学习笔记 第二十一章 排序
  • 【Java】Linux、Mac、Windows 安装 Oracle JDK
  • Android 常用命令和工具解析之内存相关
  • 深入解析自适应控制算法及python实现
  • 深入解析自校正控制(STC)算法及python实现
  • Flink转换算子——flatMap/map/filter/keyby/reduce综合案例