Optional解决什么问题?
Optional是Java 8引入的类,专门用于解决NullPointerException问题。它是一个容器对象,可以包含或不包含非空值。Optional让我们写出更优雅的空值检查代码。
生活比喻
想象你要从自动售货机买饮料。传统方式是你伸手就去拿,如果机器坏了(null),你就会摔一跤(NullPointerException)。Optional就像是给你一个"检查窗口"——先看看里面有没有饮料(有值),再决定怎么拿。如果没有,你可以选择退款(orElse)或者换一个机器(orElseGet)。
创建Optional的三种方式
import java.util.Optional;
public class OptionalCreateDemo {
public static void main(String[] args) {
// 1. 空Optional - 明确表示没有值
Optional empty = Optional.empty();
System.out.println("empty: " + empty);
System.out.println("empty有值吗: " + empty.isPresent());
// 2. 非空Optional - 不能传入null,会抛异常
Optional of = Optional.of("Hello");
System.out.println("of: " + of);
System.out.println("of有值吗: " + of.isPresent());
// 3. 可空Optional - 可以传入null,返回空Optional
Optional nullable1 = Optional.ofNullable("World");
Optional nullable2 = Optional.ofNullable(null);
System.out.println("nullable1: " + nullable1);
System.out.println("nullable2: " + nullable2);
}
}
安全获取值的方法
| 方法 | 说明 | 适用场景 |
|---|---|---|
| get() | 获取值,空则抛异常 | 确定有值时 |
| orElse(value) | 空则返回默认值(始终计算) | 简单默认值 |
| orElseGet(supplier) | 空则调用Supplier返回 | 计算代价高 |
| orElseThrow() | 空则抛出指定异常 | 空值是错误 |
| ifPresent(consumer) | 有值时执行 | 仅处理有值情况 |
orElse vs orElseGet 的区别
import java.util.Optional;
public class OrElseVsOrElseGet {
public static void main(String[] args) {
Optional opt = Optional.ofNullable("Hello");
// orElse:无论是否为空,都执行
String s1 = opt.orElse(getDefault()); // 返回"Hello",但getDefault()还是被调用了
System.out.println("s1 = " + s1);
// orElseGet:只有为空时才执行
Optional emptyOpt = Optional.ofNullable(null);
String s2 = emptyOpt.orElseGet(() -> getDefault()); // 返回"default"
System.out.println("s2 = " + s2);
}
static String getDefault() {
System.out.println("getDefault() 被调用了!");
return "default";
}
}
// 输出:
// getDefault() 被调用了! ← orElse的,即使有值也调用
// s1 = Hello
// getDefault() 被调用了! ← orElseGet的空值情况
// s2 = default
链式操作:map和flatMap
Optional的map和flatMap让我们可以链式转换值,而不需要一堆if-null检查。
import java.util.Optional;
public class OptionalMapDemo {
public static void main(String[] args) {
// map: 转换值
Optional opt = Optional.of("hello");
Optional len = opt.map(String::length);
System.out.println("字符串长度: " + len.orElse(0));
// flatMap: 转换后返回Optional
Optional email = Optional.of("user@example.com");
Optional domainLen = email
.flatMap(emailStr -> Optional.of(emailStr.split("@")[1]))
.map(domain -> domain.length());
System.out.println("域名长度: " + domainLen.orElse(0));
}
}
filter - 过滤值
import java.util.Optional;
public class OptionalFilterDemo {
public static void main(String[] args) {
Optional num = Optional.of(10);
// 过滤:满足条件保留,否则返回空
Optional filtered = num.filter(n -> n > 5);
System.out.println("10 > 5: " + filtered.orElse(-1)); // 10
Optional filtered2 = num.filter(n -> n > 100);
System.out.println("10 > 100: " + filtered2.orElse(-1)); // -1
// 应用:验证手机号
Optional phone = Optional.of("13812345678");
Optional validPhone = phone
.filter(p -> p.matches("1[3-9]\\d{9}"))
.filter(p -> p.startsWith("1"));
System.out.println("有效手机号: " + validPhone.orElse("无效"));
}
}
实战:安全获取嵌套属性
传统方式的痛苦:多层null检查
// 传统方式:噩梦般的null检查
public String getCityName(User user) {
if (user != null) {
Address address = user.getAddress();
if (address != null) {
City city = address.getCity();
if (city != null) {
return city.getName();
}
}
}
return "未知";
}
Optional的优雅方式:链式调用
// Optional方式:清晰优雅
public String getCityName(User user) {
return Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.map(City::getName)
.orElse("未知");
}
完整实战示例
import java.util.Optional;
class User {
private String name;
private int age;
private Address address;
public User(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
public Optional getNameOpt() { return Optional.ofNullable(name); }
public Optional getAgeOpt() { return Optional.ofNullable(age); }
public Optional getAddressOpt() { return Optional.ofNullable(address); }
}
class Address {
private String street;
private City city;
public Address(String street, City city) {
this.street = street;
this.city = city;
}
public Optional getStreetOpt() { return Optional.ofNullable(street); }
public Optional getCityOpt() { return Optional.ofNullable(city); }
}
class City {
private String name;
private String code;
public City(String name, String code) {
this.name = name;
this.code = code;
}
public Optional getNameOpt() { return Optional.ofNullable(name); }
public Optional getCodeOpt() { return Optional.ofNullable(code); }
}
public class OptionalDemo {
public static void main(String[] args) {
// 完整用户
User user1 = new User("张三", 25, new Address("中关村大街1号", new City("北京", "010")));
// 空地址用户
User user2 = new User("李四", 30, null);
// 完整信息用户
System.out.println("===== user1 =====");
printUserCity(user1);
printUserAreaCode(user1);
// 空地址用户
System.out.println("\n===== user2 =====");
printUserCity(user2);
printUserAreaCode(user2);
}
static void printUserCity(User user) {
String city = Optional.ofNullable(user)
.flatMap(User::getAddressOpt)
.flatMap(Address::getCityOpt)
.flatMap(City::getNameOpt)
.orElse("未知城市");
System.out.println("城市: " + city);
}
static void printUserAreaCode(User user) {
String areaCode = Optional.ofNullable(user)
.flatMap(User::getAddressOpt)
.flatMap(Address::getCityOpt)
.flatMap(City::getCodeOpt)
.orElse("000");
System.out.println("区号: " + areaCode);
}
}
注意事项
Optional不是Serializable!
Optional不能直接用于字段声明或RPC调用,如果你需要序列化,考虑使用@Nullable注解或者自己封装。
Optional不是万能的!
Optional主要用于方法返回值,表达"可能有值也可能没有"。不建议:
- 用Optional作为类的字段
- 用Optional作为方法参数
- 用Optional替代if-null检查
小结
- Optional解决NullPointerException,让空值处理更优雅
- ofNullable()创建可空Optional
- orElse()/orElseGet()提供默认值,推荐用orElseGet
- map()/flatMap()链式转换值
- filter()按条件过滤
- Optional主要用于方法返回值,不建议作为字段