教程中心 / Java教程 / Optional

Optional

10分钟 函数式编程

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主要用于方法返回值,不建议作为字段
☕ Java 在线代码编辑器
📝 运行结果