← 返回首页

Java Optional详解:优雅处理空值

📂 java ⏱ 5 min 827 words

Java Optional详解:优雅处理空值

概述

Optional是Java 8引入的容器类,用于表示一个值可能存在或不存在。Optional可以帮助避免NullPointerException,使代码更优雅、更可读。

1. 创建Optional

import java.util.Optional;

public class OptionalCreationExample {
    public static void main(String[] args) {
        // 1. Optional.of():值不能为null
        Optional<String> name = Optional.of("Alice");
        System.out.println("Of: " + name);
        
        // Optional.of(null);  // 抛出NullPointerException
        
        // 2. Optional.ofNullable():值可以为null
        Optional<String> nullName = Optional.ofNullable(null);
        System.out.println("OfNullable: " + nullName);
        
        Optional<String> nonNullName = Optional.ofNullable("Bob");
        System.out.println("OfNullable: " + nonNullName);
        
        // 3. Optional.empty():空Optional
        Optional<String> empty = Optional.empty();
        System.out.println("Empty: " + empty);
        
        // 4. Optional类的布尔方法
        System.out.println("name.isPresent(): " + name.isPresent());
        System.out.println("nullName.isPresent(): " + nullName.isPresent());
        System.out.println("empty.isPresent(): " + empty.isPresent());
    }
}

2. 使用Optional

import java.util.Optional;

public class OptionalUsageExample {
    public static void main(String[] args) {
        Optional<String> name = Optional.of("Alice");
        Optional<String> nullName = Optional.ofNullable(null);
        
        // 1. get():获取值(如果不存在会抛出异常)
        String value = name.get();
        System.out.println("Get: " + value);
        
        // 2. orElse():值不存在时返回默认值
        String value2 = nullName.orElse("Unknown");
        System.out.println("OrElse: " + value2);
        
        // 3. orElseGet():值不存在时调用Supplier获取默认值
        String value3 = nullName.orElseGet(() -> "Default");
        System.out.println("OrElseGet: " + value3);
        
        // 4. orElseThrow():值不存在时抛出异常
        try {
            String value4 = nullName.orElseThrow(() -> 
                new RuntimeException("值不存在"));
        } catch (RuntimeException e) {
            System.out.println("OrElseThrow: " + e.getMessage());
        }
        
        // 5. ifPresent():值存在时执行操作
        name.ifPresent(n -> System.out.println("IfPresent: " + n));
        nullName.ifPresent(n -> System.out.println("This won't print"));
        
        // 6. ifPresentOrElse():值存在时执行一个操作,不存在时执行另一个操作
        name.ifPresentOrElse(
            n -> System.out.println("Found: " + n),
            () -> System.out.println("Not found")
        );
        
        nullName.ifPresentOrElse(
            n -> System.out.println("Found: " + n),
            () -> System.out.println("Not found")
        );
    }
}

3. Optional转换操作

import java.util.Optional;

public class OptionalTransformExample {
    public static void main(String[] args) {
        Optional<String> name = Optional.of("Alice");
        Optional<String> nullName = Optional.ofNullable(null);
        
        // 1. map():转换值
        Optional<Integer> nameLength = name.map(String::length);
        System.out.println("Map: " + nameLength.orElse(0));
        
        Optional<Integer> nullNameLength = nullName.map(String::length);
        System.out.println("Map null: " + nullNameLength.orElse(0));
        
        // 2. flatMap():转换为Optional
        Optional<String> upperCase = name.flatMap(n -> Optional.of(n.toUpperCase()));
        System.out.println("FlatMap: " + upperCase.orElse(""));
        
        // 3. filter():过滤
        Optional<String> filtered = name.filter(n -> n.startsWith("A"));
        System.out.println("Filter: " + filtered.orElse(""));
        
        Optional<String> notFiltered = name.filter(n -> n.startsWith("B"));
        System.out.println("Filter not match: " + notFiltered.orElse(""));
        
        // 4. or():提供替代Optional(Java 9+)
        Optional<String> alternative = nullName.or(() -> Optional.of("Alternative"));
        System.out.println("Or: " + alternative.orElse(""));
        
        // 5. stream():转换为Stream(Java 9+)
        name.stream().forEach(n -> System.out.println("Stream: " + n));
        nullName.stream().forEach(n -> System.out.println("This won't print"));
    }
}

4. Optional与方法

import java.util.Optional;

public class OptionalWithMethods {
    // 返回Optional的方法
    public static Optional<String> findName(int id) {
        if (id == 1) {
            return Optional.of("Alice");
        } else if (id == 2) {
            return Optional.of("Bob");
        } else {
            return Optional.empty();
        }
    }
    
    // 使用Optional的方法
    public static void processName(int id) {
        findName(id)
            .map(String::toUpperCase)
            .ifPresent(name -> System.out.println("Name: " + name));
    }
    
    public static void main(String[] args) {
        processName(1);  // 输出: Name: ALICE
        processName(2);  // 输出: Name: BOB
        processName(3);  // 无输出
    }
}

5. Optional最佳实践

import java.util.Optional;

public class OptionalBestPractices {
    // 1. 不要用Optional作为字段
    // 错误做法
    // public class Person {
    //     private Optional<String> name;  // 不推荐
    // }
    
    // 正确做法
    public static class Person {
        private String name;
        
        public Person(String name) {
            this.name = name;
        }
        
        public Optional<String> getName() {
            return Optional.ofNullable(name);
        }
    }
    
    // 2. 不要用Optional.get()而不检查
    public static void badPractice(Optional<String> name) {
        // String value = name.get();  // 危险!可能抛出异常
    }
    
    // 3. 使用orElseThrow()而不是get()
    public static void goodPractice(Optional<String> name) {
        String value = name.orElseThrow(() -> 
            new RuntimeException("Name not found"));
    }
    
    // 4. 使用链式操作
    public static void chainOperations(Optional<Person> person) {
        person
            .map(Person::getName)
            .flatMap(name -> name)
            .map(String::toUpperCase)
            .ifPresent(name -> System.out.println("Name: " + name));
    }
    
    public static void main(String[] args) {
        // 使用示例
        Optional<Person> person = Optional.of(new Person("Alice"));
        chainOperations(person);
        
        Optional<Person> nullPerson = Optional.empty();
        chainOperations(nullPerson);
    }
}

6. 实际应用示例

用户查找服务

import java.util.Optional;

public class UserService {
    // 模拟数据库
    private static final Map<Long, User> database = new HashMap<>();
    
    static {
        database.put(1L, new User(1L, "Alice", "alice@example.com"));
        database.put(2L, new User(2L, "Bob", "bob@example.com"));
    }
    
    public Optional<User> findById(Long id) {
        return Optional.ofNullable(database.get(id));
    }
    
    public Optional<String> findEmailById(Long id) {
        return findById(id)
            .map(User::getEmail);
    }
    
    public Optional<String> findNameById(Long id) {
        return findById(id)
            .map(User::getName)
            .filter(name -> !name.isEmpty());
    }
    
    public User findUserOrDefault(Long id, String defaultName) {
        return findById(id)
            .orElse(new User(id, defaultName, "unknown@example.com"));
    }
}

class User {
    private Long id;
    private String name;
    private String email;
    
    public User(Long id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }
    
    public Long getId() { return id; }
    public String getName() { return name; }
    public String getEmail() { return email; }
    
    @Override
    public String toString() {
        return "User{id=" + id + ", name='" + name + "', email='" + email + "'}";
    }
}

// 测试
public class UserServiceTest {
    public static void main(String[] args) {
        UserService service = new UserService();
        
        // 查找用户
        Optional<User> user = service.findById(1L);
        user.ifPresent(System.out::println);
        
        // 查找邮箱
        Optional<String> email = service.findEmailById(1L);
        email.ifPresent(e -> System.out.println("Email: " + e));
        
        // 查找不存在的用户
        Optional<User> nonExistent = service.findById(999L);
        nonExistent.ifPresentOrElse(
            u -> System.out.println("Found: " + u),
            () -> System.out.println("User not found")
        );
        
        // 使用默认值
        User defaultUser = service.findUserOrDefault(999L, "Guest");
        System.out.println("Default user: " + defaultUser);
    }
}

7. 常见陷阱

import java.util.Optional;

public class OptionalPitfalls {
    public static void main(String[] args) {
        // 1. 不要用Optional作为参数
        // 错误做法
        // public void process(Optional<String> name) { }
        
        // 正确做法
        public void process(String name) {
            Optional.ofNullable(name)
                .ifPresent(this::doProcess);
        }
        
        // 2. 不要创建Optional的集合
        // 错误做法
        // List<Optional<String>> list = new ArrayList<>();
        
        // 正确做法
        // List<String> list = new ArrayList<>();
        
        // 3. 不要序列化Optional
        // Optional不是Serializable的
        
        // 4. 不要用Optional包装基本类型
        // 错误做法
        // Optional<int> number = Optional.of(5);
        
        // 正确做法
        Optional<Integer> number = Optional.of(5);
    }
    
    private void doProcess(String name) {
        System.out.println("Processing: " + name);
    }
}

8. 最佳实践总结

  1. 方法返回Optional:当方法可能返回null时,返回Optional
  2. 不要用Optional作为字段:Optional应该用于方法返回值
  3. 使用链式操作:利用map、flatMap、filter等方法
  4. 提供默认值:使用orElse、orElseGet、orElseThrow
  5. 避免Optional.get():除非已经检查了isPresent()

总结

Optional是Java 8引入的重要特性,它帮助我们优雅地处理空值。通过Optional,可以避免NullPointerException,使代码更安全、更可读。掌握Optional的使用方法和最佳实践,是现代Java编程的重要技能。