Java Optional案例

wen java案例 1

本文目录导读:

Java Optional案例

  1. 基础创建与获取(创建方式)
  2. 使用 orElse / orElseGet / orElseThrow(核心用法)
  3. 使用 ifPresent / ifPresentOrElse(消费)
  4. 使用 map 和 flatMap(转换链式操作)
  5. 使用 filter(过滤)
  6. 使用 stream(与 Stream 结合,Java 9+)
  7. 综合案例:用户地址查询(模拟真实业务)
  8. 反模式与注意事项(重要!刻意练习避免)
  9. JDK 版本差异总结
  10. 核心方法论总结
  11. 关键原则

Java Optional 是 Java 8 引入的一个容器类,用于优雅地处理可能为空的对象,避免 NullPointerException

以下是各种场景下的使用案例,从简单到复杂,请认真阅读注释(注:):


基础创建与获取(创建方式)

import java.util.Optional;
public class CreateOptional {
    public static void main(String[] args) {
        // 1. 创建空的 Optional
        Optional<String> emptyOpt = Optional.empty();
        // 2. 创建非空的 Optional(传入 null 会抛异常 NPE)
        Optional<String> nonEmptyOpt = Optional.of("Hello World");
        // 3. 创建允许为 null 的 Optional(推荐)
        Optional<String> nullableOpt = Optional.ofNullable(getDataMayBeNull());
        System.out.println("emptyOpt 是否存在值: " + emptyOpt.isPresent());
        System.out.println("nonEmptyOpt 的值: " + nonEmptyOpt.get());
        System.out.println("nullableOpt 的值: " + nullableOpt.orElse("默认值"));
        // 注:Optional.of(null) 会直接抛出 NullPointerException
        // Optional.of(null); // 异常
    }
    private static String getDataMayBeNull() {
        return null; // 模拟可能返回 null 的情况
    }
}

使用 orElse / orElseGet / orElseThrow(核心用法)

import java.util.Optional;
public class OrElseDemo {
    public static void main(String[] args) {
        String name = null;
        // 1. orElse:如果为空,返回默认值(默认值会提前创建)
        String result1 = Optional.ofNullable(name)
                .orElse("默认值");
        System.out.println("orElse结果: " + result1);
        // 2. orElseGet:如果为空,通过 Supplier 生成默认值(懒加载)
        String result2 = Optional.ofNullable(name)
                .orElseGet(() -> "由Supplier生成的默认值");
        System.out.println("orElseGet结果: " + result2);
        // 3. orElseThrow:如果为空,抛出指定的异常(Java 10+ 可带参数)
        try {
            Optional.ofNullable(name)
                    .orElseThrow(() -> new IllegalArgumentException("name 不能为空"));
        } catch (Exception e) {
            System.out.println("捕获异常: " + e.getMessage());
        }
        // 4. Java 10 新特性:orElseThrow() 无参,抛出 NoSuchElementException
        // Optional.ofNullable(name).orElseThrow();
    }
}

使用 ifPresent / ifPresentOrElse(消费)

import java.util.Optional;
public class IfPresentDemo {
    public static void main(String[] args) {
        String username = "张三";
        // 1. ifPresent:值存在时执行动作
        Optional.ofNullable(username)
                .ifPresent(u -> System.out.println("用户名: " + u));
        // 2. ifPresentOrElse(Java 9+):存在时执行动作,否则执行备用动作
        Optional.ofNullable(null)
                .ifPresentOrElse(
                        u -> System.out.println("用户名: " + u),
                        () -> System.out.println("用户名为空,执行备用逻辑")
                );
        // 3. 传统写法对比(啰嗦且易出错)
        if (username != null) {
            System.out.println("传统写法用户名: " + username);
        }
    }
}

使用 map 和 flatMap(转换链式操作)

import java.util.Optional;
public class MapDemo {
    public static void main(String[] args) {
        Person person = new Person("Alice", new Address("北京", "朝阳区"));
        // 1. map:将 Optional 包装的值转换为另一种值
        // 场景:获取用户的城市(可能为空)
        String city = Optional.ofNullable(person)
                .map(Person::getAddress)       // 返回 Optional<Address>
                .map(Address::getCity)         // 返回 Optional<String>
                .orElse("未知城市");
        System.out.println("城市: " + city);
        // 2. flatMap:如果映射结果已经是 Optional,避免嵌套 Optional
        // 传统使用 map 会得到 Optional<Optional<String>>
        Optional<Optional<String>> nestedOpt = Optional.ofNullable(person)
                .map(Person::getAddress)
                .map(address -> Optional.ofNullable(address.getStreet()));
        // 使用 flatMap 扁平化
        String street = Optional.ofNullable(person)
                .map(Person::getAddress)
                .flatMap(address -> Optional.ofNullable(address.getStreet()))
                .orElse("未知街道");
        System.out.println("街道: " + street);
        // 3. 多个步骤的流水线
        String detail = Optional.ofNullable(person)
                .map(Person::getAddress)
                .map(a -> a.getCity() + "-" + a.getStreet())
                .orElse("无地址");
        System.out.println("完整地址: " + detail);
        // 4. 链式判空(多次转换不会 NPE)
        String result = Optional.ofNullable(person)
                .map(Person::getAddress)          // 若 person 为 null,返回 Optional.empty()
                .map(Address::getCity)            // 若 address 为 null,返回 Optional.empty()
                .map(String::toUpperCase)         // 若 city 为 null,返回 Optional.empty()
                .orElse("DEFAULT");
        System.out.println("链式结果: " + result);
    }
}
class Person {
    private String name;
    private Address address;
    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }
    public Address getAddress() {
        return address;
    }
    // getter & setter 省略
}
class Address {
    private String city;
    private String street;
    public Address(String city, String street) {
        this.city = city;
        this.street = street;
    }
    public String getCity() {
        return city;
    }
    public String getStreet() {
        return street;
    }
}

使用 filter(过滤)

import java.util.Optional;
public class FilterDemo {
    public static void main(String[] args) {
        Integer score = 85;
        // 1. 过滤:分数大于 90 才显示
        Optional.ofNullable(score)
                .filter(s -> s > 90)
                .ifPresentOrElse(
                        s -> System.out.println("优秀学生成绩: " + s),
                        () -> System.out.println("成绩未达到90分")
                );
        // 2. 多条件过滤(使用 && 连接)
        Optional.ofNullable(score)
                .filter(s -> s >= 60 && s <= 100)   // 合格范围
                .filter(s -> s > 80)                // 又是优秀
                .ifPresent(s -> System.out.println("优良成绩: " + s));
        // 3. 与 map 组合:过滤然后转换
        String grade = Optional.ofNullable(score)
                .filter(s -> s >= 0 && s <= 100)
                .map(s -> s >= 90 ? "A" : s >= 80 ? "B" : s >= 70 ? "C" : "D")
                .orElse("无效成绩");
        System.out.println("成绩等级: " + grade);
    }
}

使用 stream(与 Stream 结合,Java 9+)

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class StreamCombineDemo {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add(null);
        names.add("Bob");
        names.add("");
        // 1. 过滤掉 null 和空字符串
        List<String> validNames = names.stream()
                .map(Optional::ofNullable)              // 包装成 Optional
                .flatMap(Optional::stream)              // 扁平化流(空 Optional 自动跳过)
                .filter(name -> !name.isEmpty())
                .collect(Collectors.toList());
        System.out.println("有效名称: " + validNames);
        // 2. 使用 Optional 简化 null 判断
        List<String> cities = new ArrayList<>();
        cities.add("北京");
        cities.add(null);
        cities.add("上海");
        cities.stream()
                .map(Optional::ofNullable)
                .flatMap(Optional::stream)
                .forEach(System.out::println);
        // 3. 传统方式对比(大量 if 判断)
        for (String name : names) {
            if (name != null && !name.isEmpty()) {
                System.out.println("传统输出: " + name);
            }
        }
    }
}

综合案例:用户地址查询(模拟真实业务)

import java.util.Optional;
public class UserService {
    public static void main(String[] args) {
        // 模拟 MySQL 查询结果
        User user = findUserById(1);  // 可能返回 null
        // 业务需求:获取用户所在城市,如果没有用户或地址,打印"未知"
        String city = Optional.ofNullable(user)
                .map(User::getAddress)              // 获取地址
                .map(Address2::getCity)             // 获取城市
                .orElse("未知");
        System.out.println("用户所在城市: " + city);
        // 业务需求:如果是北京用户,打印欢迎语;否则打印其他
        Optional.ofNullable(user)
                .map(User::getAddress)
                .map(Address2::getCity)
                .filter(c -> "北京".equals(c))
                .ifPresentOrElse(
                        c -> System.out.println("欢迎来到北京!"),
                        () -> System.out.println("您好!")
                );
        // 业务需求:获取用户的第一个亲戚的名字,否则默认"无亲戚"
        String relative = Optional.ofNullable(user)
                .map(User::getRelative)
                .map(Relative::getName)
                .orElse("无亲戚");
        System.out.println("亲戚: " + relative);
    }
    private static User findUserById(int id) {
        // 模拟数据库返回 null
        return new User(new Address2("上海", "浦东"), new Relative("王五"));
    }
}
class User {
    private Address2 address;
    private Relative relative;
    public User(Address2 address, Relative relative) {
        this.address = address;
        this.relative = relative;
    }
    public Address2 getAddress() {
        return address;
    }
    public Relative getRelative() {
        return relative;
    }
}
class Address2 {
    private String city;
    private String street;
    public Address2(String city, String street) {
        this.city = city;
        this.street = street;
    }
    public String getCity() {
        return city;
    }
}
class Relative {
    private String name;
    public Relative(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

反模式与注意事项(重要!刻意练习避免)

import java.util.Optional;
public class AntiPatternsDemo {
    public static void main(String[] args) {
        String name = null;
        // ❌ 反模式1:调用 get() 不判空(会抛 NoSuchElementException)
        // String dangerous = Optional.ofNullable(name).get(); // 异常!
        // ✅ 正确做法
        String safe = Optional.ofNullable(name).orElse("默认");
        // ❌ 反模式2:用 Optional 作为方法参数(不推荐,仍可能传 null)
        // void setName(Optional<String> name) { ... }  // 不推荐
        // ✅ 推荐:方法参数直接传 null 即可,方法内部用 Optional 处理
        // void setName(String name) { 
        //     this.name = Optional.ofNullable(name).orElse("默认名");
        // }
        // ❌ 反模式3:滥用 isPresent() + get() 替代传统 if
        if (Optional.ofNullable(name).isPresent()) {
            // 仍然存在空值风险,不如直接用 if (name != null)
        }
        // ✅ 正确:使用 ifPresent 或 map + orElse
        Optional.ofNullable(name).ifPresent(n -> System.out.println("名字: " + n));
        // ❌ 反模式4:将 Optional 用作集合元素/Map键值
        // Map<String, Optional<String>> map = ...; // 不建议
        // ✅ 正确:Map 值直接用 null 表示不存在
        // Map<String, String> map = new HashMap<>();
    }
}

JDK 版本差异总结

// JDK 8: 
//   - Optional.empty(), Optional.of(), Optional.ofNullable()
//   - get(), orElse(), orElseGet(), orElseThrow()
//   - ifPresent(), map(), flatMap(), filter()
//   - isPresent()
// 
// JDK 9+ 新增:
//   - ifPresentOrElse(consumer, runnable)
//   - or(Supplier<Optional<T>>)
//   - stream()
//  
// JDK 10+ 新增:
//   - orElseThrow() 无参版本(更简洁)

核心方法论总结

场景 推荐写法
需要一个默认值 orElse("默认值")orElseGet(() -> ...)
需要抛异常 orElseThrow(IllegalArgumentException::new)
有值则消费 ifPresent(...)ifPresentOrElse(...)
链式获取属性 map(...).map(...).orElse(...)
过滤条件 filter(...).orElse(...)
与集合结合 flatMap(Optional::stream)

关键原则

  1. 不要对 get() 不加保护地调用 —— 极易抛异常。
  2. 不要用 Optional 作为字段、方法参数、集合元素 —— 会破坏代码可读性和性能。
  3. Optional 是给返回类型用的,不是为了替代所有 null 判空。
  4. 优先使用 ifPresentmaporElse,减少显式条件判断。
  5. Java 9+ 中,可以用 ifPresentOrElse 替代传统 if-else
  6. 谨慎使用 map 返回 Optional 时,考虑 flatMap 避免嵌套。

通过以上案例,你应该能熟练用 Optional 写出健壮、简洁、无空指针异常的代码!

上一篇groupingBy案例

下一篇reduce案例

抱歉,评论功能暂时关闭!