本文最后更新于 2025-03-07,文章超过7天没更新,应该是已完结了~

一、Optional是什么?

你有没有遇到过代码里突然蹦出个“空指针异常”(NullPointerException),然后一脸懵逼地去找哪里出了问题?Java里的Optional就是为了解决这个问题来的。它就像一个“盒子”,专门用来装可能会为空的东西。它逼着你老老实实面对“这个东西可能是空的”这件事,而不是稀里糊涂地用下去,最后程序崩了才后悔。

简单说,Optional是个容器,里面可以装任何东西(比如字符串、数字、对象),也可以啥都不装(null)。它提供了一堆方便的方法,让你操作这个盒子里的东西时更安全。比如,你可以用它的方法检查盒子里有没有货,或者没货时给个默认值,甚至直接报错提醒自己。

而且,Optional还能跟现在流行的“函数式编程”搭上关系。你可以用Lambda表达式或者Stream这些酷炫的东西跟它玩在一起,写出来的代码看起来更简洁,不用老写一堆if (东西 != null)这种烦人的检查。


二、Optional对象的方法

咱们先搭个小场景,假设有两个类:一个是User(用户),一个是UserRepository(用户仓库)。User就是个简单的人,有name(名字)和fullName(全名);UserRepository是个找人的地方,你给它一个名字,它帮你找对应的人,找不到就返回空。

1. 检查盒子里有没有东西:isPresent 和 isEmpty

  • isPresent:问“盒子里有东西吗?”有就返回true,没有就false。

  • isEmpty:问“盒子是空的吗?”空就true,不空就false。

例子:

Optional<Object> box = Optional.empty(); // 造一个空盒子 
System.out.println(box.isPresent()); // false,啥也没有 
System.out.println(box.isEmpty()); // true,确实是空的

2. 怎么造盒子:empty、of、ofNullable

  • empty():造一个空盒子,啥也不装。

Optional<Object> box = Optional.empty(); // 空的,很干脆
  • of():装个肯定不为空的东西进去。如果东西是空的(null),它会生气,直接抛异常(NullPointerException)。

String name = "Peter"; 

Optional<String> box = Optional.of(name); // 装了个"Peter",放心用
  • ofNullable():装个可能为空的东西。如果是null也没关系,不会发脾气。

String name = null; 

Optional<String> box = Optional.ofNullable(name); // 装了个null,没事

3. 怎么拿东西:get、orElse、orElseGet、orElseThrow

  • get():直接掏盒子里的东西。如果盒子是空的,它会报错(NoSuchElementException),提醒你“没货啊!”。

Optional<String> box = Optional.of("Peter"); String name = box.get(); // 拿到"Peter"
  • orElse():掏东西,没货就给个备胎。

UserRepository repo = new UserRepository(); 

Optional<User> user = repo.findUserByName("Peter2"); // 找不到这个人 

User backup = user.orElse(new User("Stark", "Tony Stark")); // 用备胎Tony Stark
  • orElseGet():跟orElse差不多,但更聪明。备胎只有在真没货时才会被造出来,省资源。

User backup = user.orElseGet(() -> new User("Stark", "Tony Stark")); // 没货才造Tony
  • orElseThrow():没货就发脾气,扔个你指定的异常出来。

user.orElseThrow(() -> new RuntimeException("找不到人!")); // 没货就报错

4. 判断后干活:ifPresent、ifPresentOrElse、filter

  • ifPresent():盒子里有货就干活,没货就啥也不干,不报错。

Optional<User> user = repo.findUserByName("Peter2"); 

user.ifPresent(u -> System.out.println(u.getFullName())); // 没货就安静地啥也不干
  • ifPresentOrElse():有货干这个,没货干那个。

user.ifPresentOrElse( u -> System.out.println(u.getFullName()), // 有货打印全名 
() -> System.out.println("找不到人")); // 没货说句话
  • filter():检查盒子里的东西符不符合条件,符合就留着,不符合就变空盒子。

Optional<User> user = repo.findUserByName("Peter"); 
Optional<User> filtered = user.filter(u -> u.getFullName().equals("Peter Parker")); 
System.out.println(filtered.isPresent()); // true,Peter Parker符合条件

5. 转换东西:map 和 flatMap

  • 假设getFullName返回的是Optional<String>:

public Optional<String> getFullName() { return Optional.ofNullable(fullName); }
  • map():把盒子里的东西拿出来加工一下,再装回新盒子。没货就不加工。

Optional<User> user = repo.findUserByName("Peter"); 

Optional<String> fullName = user.map(User::getFullName); // 拿到Optional<Peter Parker>
  • flatMap():专门对付盒子里还有盒子的情况,把嵌套的盒子压平。--->比map多拆一层

Optional<String> fullName = user.flatMap(User::getFullName); // 直接拿到Peter Parker

区别:map适合简单加工,flatMap适合处理嵌套的Optional。

6. 变成流水线:stream

把Optional变成Stream,方便做流操作。有货就加工,没货就空流水线。

Optional<User> user = repo.findUserByName("Peter"); 

user.map(User::getName).stream().forEach(System.out::println); // 输出"Peter"

三、啥时候别用Optional?

  • 别用在类的字段里:会占更多内存,还让序列化变麻烦。

  • 别用在方法参数里:会让方法不好懂,用起来也麻烦。

  • 别用在构造器参数里:强迫别人造Optional,还不如多写几个构造器。

  • 别用在集合里:集合本身就能处理空的情况,不需要再包一层。

  • 别老用get():没货就报错,还不如用orElse这种安全的。