Optional案例

wen java案例 3

本文目录导读:

Optional案例

  1. Optional概述
  2. 基础使用案例
  3. 获取值的案例
  4. 实战案例
  5. Optional最佳实践
  6. 实际项目中的综合案例
  7. Optional核心方法总结

我来为您详细讲解Java 8的Optional类,包括概念、核心用法、实战案例和最佳实践。

Optional概述

Optional是一个容器对象,用于包含可能为空的值,它提供了一种更优雅的方式来处理空值,避免NullPointerException

基础使用案例

1 创建Optional对象

import java.util.Optional;
public class CreateOptionalExample {
    public static void main(String[] args) {
        // 1. 创建包含值的Optional
        Optional<String> name = Optional.of("张三");
        System.out.println("name: " + name); // Optional[张三]
        // 2. 创建空的Optional
        Optional<String> empty = Optional.empty();
        System.out.println("empty: " + empty); // Optional.empty
        // 3. 创建可能为空的Optional
        String nullable = null;
        Optional<String> optional = Optional.ofNullable(nullable);
        System.out.println("optional: " + optional); // Optional.empty
        // 注意:Optional.of()不能传null
        try {
            Optional.of(null);
        } catch (NullPointerException e) {
            System.out.println("Optional.of(null) 抛出 NullPointerException");
        }
    }
}

2 检查值是否存在

import java.util.Optional;
public class CheckValueExample {
    public static void main(String[] args) {
        Optional<String> name = Optional.of("李四");
        Optional<String> empty = Optional.empty();
        // isPresent() - 检查是否有值
        System.out.println("name.isPresent(): " + name.isPresent()); // true
        System.out.println("empty.isPresent(): " + empty.isPresent()); // false
        // isEmpty() - Java 11+ 检查是否为空
        // System.out.println("empty.isEmpty(): " + empty.isEmpty()); // true
        // ifPresent() - 如果有值则执行操作
        name.ifPresent(value -> System.out.println("值存在: " + value));
        empty.ifPresent(value -> System.out.println("永远不会执行"));
    }
}

获取值的案例

1 各种获取值的方式

import java.util.NoSuchElementException;
import java.util.Optional;
public class GetValueExample {
    public static void main(String[] args) {
        Optional<String> name = Optional.of("王五");
        Optional<String> empty = Optional.empty();
        // 1. get() - 如果值不存在会抛异常(不推荐使用)
        System.out.println("get(): " + name.get()); // 王五
        try {
            empty.get();
        } catch (NoSuchElementException e) {
            System.out.println("empty.get() 抛出 NoSuchElementException");
        }
        // 2. orElse() - 如果值不存在返回默认值(推荐)
        System.out.println("orElse: " + empty.orElse("默认值")); // 默认值
        // 3. orElseGet() - 如果值不存在,通过Supplier提供默认值
        System.out.println("orElseGet: " + empty.orElseGet(() -> "懒加载默认值"));
        // 4. orElseThrow() - 如果值不存在抛出指定异常
        try {
            empty.orElseThrow(() -> new IllegalArgumentException("值不存在"));
        } catch (IllegalArgumentException e) {
            System.out.println("抛出自定义异常: " + e.getMessage());
        }
        // 5. orElseThrow() 无参数版本(Java 10+)
        try {
            empty.orElseThrow();
        } catch (NoSuchElementException e) {
            System.out.println("抛出默认 NoSuchElementException");
        }
    }
}

实战案例

1 用户信息查询系统

import java.util.Optional;
public class UserService {
    // 用户数据模拟
    private static final User[] USERS = {
        new User(1L, "张三", "zhangsan@example.com", "北京"),
        new User(2L, "李四", null, "上海"),
        new User(3L, "王五", "wangwu@example.com", null)
    };
    // 查询用户(传统方式 vs Optional方式)
    public static Optional<User> findUserById(Long id) {
        for (User user : USERS) {
            if (user.getId().equals(id)) {
                return Optional.ofNullable(user);
            }
        }
        return Optional.empty();
    }
    // 获取用户邮箱(安全获取,不抛异常)
    public String getUserEmail(Long userId) {
        return findUserById(userId)
            .map(User::getEmail)
            .orElse("无邮箱信息");
    }
    // 获取用户城市(链式操作)
    public String getUserCity(Long userId) {
        return findUserById(userId)
            .map(User::getCity)
            .filter(city -> city.length() > 0)
            .orElse("未知城市");
    }
    // 复杂业务:发送邮件
    public void sendEmailToUser(Long userId) {
        findUserById(userId)
            .filter(user -> user.getEmail() != null)
            .map(User::getEmail)
            .ifPresentOrElse(
                email -> System.out.println("发送邮件到: " + email),
                () -> System.out.println("用户无邮箱,无法发送")
            );
    }
    // 用户类
    static class User {
        private Long id;
        private String name;
        private String email;
        private String city;
        public User(Long id, String name, String email, String city) {
            this.id = id;
            this.name = name;
            this.email = email;
            this.city = city;
        }
        public Long getId() { return id; }
        public String getName() { return name; }
        public String getEmail() { return email; }
        public String getCity() { return city; }
        @Override
        public String toString() {
            return "User{id=" + id + ", name='" + name + "'}";
        }
    }
    public static void main(String[] args) {
        UserService service = new UserService();
        // 测试各种场景
        System.out.println("=== 用户邮箱查询 ===");
        System.out.println("用户1邮箱: " + service.getUserEmail(1L)); // 有邮箱
        System.out.println("用户2邮箱: " + service.getUserEmail(2L)); // 无邮箱
        System.out.println("用户99邮箱: " + service.getUserEmail(99L)); // 不存在
        System.out.println("\n=== 用户城市查询 ===");
        System.out.println("用户1城市: " + service.getUserCity(1L));
        System.out.println("用户3城市: " + service.getUserCity(3L)); // 城市为null
        System.out.println("用户99城市: " + service.getUserCity(99L)); // 用户不存在
        System.out.println("\n=== 发送邮件测试 ===");
        service.sendEmailToUser(1L); // 有邮箱
        service.sendEmailToUser(2L); // 无邮箱
        service.sendEmailToUser(99L); // 用户不存在
    }
}

2 配置管理系统

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class ConfigManager {
    private static final Map<String, String> CONFIG = new HashMap<>();
    static {
        CONFIG.put("app.name", "MyApp");
        CONFIG.put("app.version", "1.0.0");
        CONFIG.put("app.port", "8080");
    }
    // 获取配置项
    public Optional<String> getConfig(String key) {
        return Optional.ofNullable(CONFIG.get(key));
    }
    // 获取数值类型配置
    public Optional<Integer> getIntegerConfig(String key) {
        return getConfig(key)
            .map(value -> {
                try {
                    return Integer.parseInt(value);
                } catch (NumberFormatException e) {
                    return null;
                }
            });
    }
    // 获取端口(带默认值)
    public int getPort() {
        return getIntegerConfig("app.port")
            .filter(port -> port > 0 && port < 65536)
            .orElse(8080);
    }
    public static void main(String[] args) {
        ConfigManager config = new ConfigManager();
        System.out.println("应用名称: " + config.getConfig("app.name").orElse("未设置"));
        System.out.println("应用端口: " + config.getPort());
        System.out.println("最大连接数: " + 
            config.getIntegerConfig("max.connections").orElse(100));
        // 链式配置读取
        String databaseUrl = config.getConfig("database.url")
            .orElseGet(() -> "jdbc:mysql://localhost:3306/default");
        System.out.println("数据库URL: " + databaseUrl);
    }
}

3 复杂的流式处理

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class OptionalStreamExample {
    public static void main(String[] args) {
        List<Employee> employees = Arrays.asList(
            new Employee("张三", 8000, "技术部"),
            new Employee("李四", 6000, "市场部"),
            new Employee("王五", null, "技术部"),
            new Employee("赵六", 15000, "管理部"),
            new Employee("钱七", 12000, null)
        );
        // 1. 过滤出有薪水的员工,并计算总薪水
        double totalSalary = employees.stream()
            .map(Employee::getSalary)
            .filter(Optional::isPresent)
            .mapToDouble(Optional::get)
            .sum();
        System.out.println("总薪水: " + totalSalary);
        // 2. 使用flatMap处理Optional
        double totalSalary2 = employees.stream()
            .map(Employee::getSalary)
            .flatMap(Optional::stream)
            .mapToDouble(Double::doubleValue)
            .sum();
        System.out.println("总薪水(flatMap): " + totalSalary2);
        // 3. 获取所有有部门名称的员工
        List<String> departments = employees.stream()
            .map(Employee::getDepartment)
            .flatMap(Optional::stream)
            .distinct()
            .collect(Collectors.toList());
        System.out.println("存在部门: " + departments);
        // 4. 查找薪水最高的员工
        Employee highestPaid = employees.stream()
            .filter(emp -> emp.getSalary().isPresent())
            .max((e1, e2) -> e1.getSalary().orElse(0.0)
                .compareTo(e2.getSalary().orElse(0.0)))
            .orElse(null);
        System.out.println("最高薪员工: " + highestPaid);
    }
    static class Employee {
        private String name;
        private Double salary;
        private String department;
        public Employee(String name, Double salary, String department) {
            this.name = name;
            this.salary = salary;
            this.department = department;
        }
        public String getName() { return name; }
        public Optional<Double> getSalary() {
            return Optional.ofNullable(salary);
        }
        public Optional<String> getDepartment() {
            return Optional.ofNullable(department);
        }
        @Override
        public String toString() {
            return "Employee{" + name + ", " + 
                   (salary != null ? salary : "无") + "}";
        }
    }
}

Optional最佳实践

1 应该避免的做法

public class OptionalAntiPatterns {
    // 反模式1:使用isPresent()和get()(没有发挥Optional的优势)
    public String badPractice1(Optional<String> optional) {
        if (optional.isPresent()) {
            return optional.get();
        }
        return "default";
    }
    // 反模式2:作为方法参数(应使用重载或明确的null检查)
    public void badPractice2(Optional<String> name) {
        // 不推荐在参数中使用Optional
    }
    // 反模式3:作为类的字段(序列化问题)
    static class BadEntity {
        private Optional<String> name; // 不应该这样做
    }
    // 反模式4:在方法链中过度使用Optional
    public String badPractice4(String value) {
        return Optional.ofNullable(value)
            .map(String::trim)
            .map(String::toUpperCase)
            .orElse("");
    }
    // 反模式的替代方案
    public String goodPractice(String value) {
        // 直接用if判断,代码更清晰
        if (value == null || value.trim().isEmpty()) {
            return "";
        }
        return value.trim().toUpperCase();
    }
    // 反模式5:用Optional包装已经可能为null的返回值
    public static class UserRepository {
        // 错误示范
        public Optional<User> findByIdBad(Long id) {
            User user = databaseQuery(id); // 这里的user已经可能为null
            return Optional.ofNullable(user);
        }
        // 正确方式:如果数据库的方法本身返回Optional
        public Optional<User> findByIdGood(Long id) {
            // 如果数据库查询API返回Optional,直接返回
            return databaseQueryOptional(id);
        }
        private User databaseQuery(Long id) { return null; }
        private Optional<User> databaseQueryOptional(Long id) { 
            return Optional.empty(); 
        }
    }
}

2 正确的使用模式

import java.util.Optional;
public class OptionalBestPractices {
    // 最佳实践1:返回值使用Optional(明确表达可能为空)
    public Optional<String> findUserName(Long userId) {
        // 假设从数据库查询
        String name = queryDatabase(userId);
        return Optional.ofNullable(name);
    }
    // 最佳实践2:使用orElseThrow处理必须存在的值
    public String getRequiredConfig(String key) {
        return Optional.ofNullable(System.getProperty(key))
            .orElseThrow(() -> new IllegalStateException("必须配置: " + key));
    }
    // 最佳实践3:链式转换和过滤
    public String transformValue(Optional<String> input) {
        return input
            .map(String::trim)
            .filter(s -> !s.isEmpty())
            .orElse("default");
    }
    // 最佳实践4:使用flatMap避免嵌套Optional
    public Optional<String> getNestedValue(Optional<Wrapper> wrapper) {
        return wrapper.flatMap(Wrapper::getValue);
    }
    // 最佳实践5:ifPresentOrElse处理存在和不存在的情况
    public void processOptional(Optional<String> optional) {
        optional.ifPresentOrElse(
            value -> System.out.println("处理: " + value),
            () -> System.out.println("值为空时的处理")
        );
    }
    private String queryDatabase(Long id) {
        return null; // 模拟不存在的用户
    }
    static class Wrapper {
        private String value;
        public Optional<String> getValue() {
            return Optional.ofNullable(value);
        }
    }
    public static void main(String[] args) {
        OptionalBestPractices example = new OptionalBestPractices();
        example.processOptional(Optional.of("测试"));
        example.processOptional(Optional.empty());
    }
}

实际项目中的综合案例

import java.util.*;
public class OrderService {
    // 订单状态
    enum OrderStatus {
        PENDING, PAID, SHIPPED, DELIVERED, CANCELLED
    }
    static class Address {
        private String street;
        private String city;
        public Address(String street, String city) {
            this.street = street;
            this.city = city;
        }
        public String getStreet() { return street; }
        public String getCity() { return city; }
        @Override
        public String toString() {
            return street + ", " + city;
        }
    }
    static class Customer {
        private Long id;
        private String name;
        private Address address; // 可能为null
        public Customer(Long id, String name, Address address) {
            this.id = id;
            this.name = name;
            this.address = address;
        }
        public Long getId() { return id; }
        public String getName() { return name; }
        public Optional<Address> getAddress() {
            return Optional.ofNullable(address);
        }
    }
    static class Order {
        private String orderId;
        private Customer customer;
        private double amount;
        private OrderStatus status;
        private Date deliveryDate; // 可能为null
        public Order(String orderId, Customer customer, double amount, 
                    OrderStatus status, Date deliveryDate) {
            this.orderId = orderId;
            this.customer = customer;
            this.amount = amount;
            this.status = status;
            this.deliveryDate = deliveryDate;
        }
        public String getOrderId() { return orderId; }
        public Optional<Customer> getCustomer() { 
            return Optional.ofNullable(customer); 
        }
        public double getAmount() { return amount; }
        public Optional<OrderStatus> getStatus() { 
            return Optional.ofNullable(status); 
        }
        public Optional<Date> getDeliveryDate() { 
            return Optional.ofNullable(deliveryDate); 
        }
    }
    private List<Order> orders = new ArrayList<>();
    public OrderService() {
        // 初始化测试数据
        Customer customer1 = new Customer(1L, "张三", 
            new Address("中关村大街1号", "北京"));
        Customer customer2 = new Customer(2L, "李四", null);
        orders.add(new Order("ORD001", customer1, 1999.00, 
            OrderStatus.DELIVERED, new Date()));
        orders.add(new Order("ORD002", customer2, 399.00, 
            OrderStatus.PAID, null));
        orders.add(new Order("ORD003", null, 899.00, 
            OrderStatus.PENDING, new Date()));
    }
    // 根据订单号查找订单
    public Optional<Order> findOrderById(String orderId) {
        return orders.stream()
            .filter(order -> order.getOrderId().equals(orderId))
            .findFirst();
    }
    // 获取订单的交付地址
    public String getDeliveryAddress(String orderId) {
        return findOrderById(orderId)
            .flatMap(Order::getCustomer)
            .flatMap(Customer::getAddress)
            .map(Address::toString)
            .orElse("无交付地址");
    }
    // 检查订单是否可取消
    public boolean isOrderCancellable(String orderId) {
        return findOrderById(orderId)
            .map(Order::getStatus)
            .flatMap(status -> status)
            .map(status -> status == OrderStatus.PENDING || 
                          status == OrderStatus.PAID)
            .orElse(false);
    }
    // 发送订单通知
    public void sendOrderNotification(String orderId) {
        Optional<Order> order = findOrderById(orderId);
        order.ifPresentOrElse(
            ord -> {
                String customerName = ord.getCustomer()
                    .map(Customer::getName)
                    .orElse("未知客户");
                System.out.println("发送订单通知给: " + customerName);
                ord.getDeliveryDate().ifPresent(
                    date -> System.out.println("预计交付: " + date)
                );
            },
            () -> System.out.println("订单不存在: " + orderId)
        );
    }
    // 统计所有有交付日期的订单总额
    public double calculateDeliveredAmount() {
        return orders.stream()
            .filter(order -> order.getStatus()
                .map(s -> s == OrderStatus.DELIVERED)
                .orElse(false))
            .filter(order -> order.getDeliveryDate().isPresent())
            .mapToDouble(Order::getAmount)
            .sum();
    }
    public static void main(String[] args) {
        OrderService service = new OrderService();
        System.out.println("=== 订单地址查询 ===");
        System.out.println("ORD001地址: " + service.getDeliveryAddress("ORD001"));
        System.out.println("ORD002地址: " + service.getDeliveryAddress("ORD002"));
        System.out.println("ORD003地址: " + service.getDeliveryAddress("ORD003"));
        System.out.println("ORD999地址: " + service.getDeliveryAddress("ORD999"));
        System.out.println("\n=== 订单取消检查 ===");
        System.out.println("ORD001可取消: " + service.isOrderCancellable("ORD001"));
        System.out.println("ORD002可取消: " + service.isOrderCancellable("ORD002"));
        System.out.println("ORD003可取消: " + service.isOrderCancellable("ORD003"));
        System.out.println("\n=== 发送通知 ===");
        service.sendOrderNotification("ORD001");
        service.sendOrderNotification("ORD002");
        service.sendOrderNotification("ORD999");
        System.out.println("\n=== 统计 ===");
        System.out.println("已交付订单总额: " + service.calculateDeliveredAmount());
    }
}

Optional核心方法总结

方法 说明 返回值
empty() 创建空Optional Optional<T>
of(value) 创建包含非null值的Optional Optional<T>
ofNullable(value) 创建可能为null的Optional Optional<T>
isPresent() 判断是否有值 boolean
ifPresent(consumer) 如果有值则执行操作 void
ifPresentOrElse(consumer, runnable) 有值/无值分别执行 void
get() 获取值(无值抛异常) T
orElse(default) 获取值或返回默认值 T
orElseGet(supplier) 获取值或计算默认值 T
orElseThrow(exception) 获取值或抛异常 T
map(mapper) 转换值 Optional<U>
flatMap(mapper) 转换值(不嵌套) Optional<U>
filter(predicate) 过滤值 Optional<T>
stream() 转换为流(Java 9+) Stream<T>

这个案例覆盖了Optional在实际开发中的主要使用场景,使用Optional可以让代码更安全、更清晰,但也要注意避免过度使用,在合适的场景下使用Optional是提升代码质量的好方法。

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