炫技亮点 使用Optional类优化代码,提升可读性和简化空值处理

背景

在日常的软件开发中,我们经常需要处理可能为空的值,例如从数据库查询数据调用外部接口获取数据从配置文件读取配置项等。传统的处理方式往往需要使用繁琐的空值判断和异常处理代码,使得代码变得冗长和难以理解。为了解决这个问题,Java 8 引入了 Optional 类,它提供了一种优雅和简洁的方式来处理可能为空的值,提升代码的可读性和简化空值处理。

Optional 简介

Optional 是 Java 8 中引入的一个类,它的主要目的是解决空值处理的问题。Optional 可以包装一个值,这个值可以是 null,也可以是非 null。通过使用 Optional,我们可以避免显式的空值判断,从而简化代码逻辑。

Optional类有两种状态:

  • 存在值的状态,表示对象不为空。
  • 空值状态,表示对象为空。

优化代码示例

让我们从一个简单的示例开始,假设我们有一个 User 类,其中包含一个可能为 null 的 name 属性。我们希望获取该用户的名字,并在名字存在时打印出来。下面是一个未优化的代码示例:

User user = getUser();
if (user != null) {
    
    
    String name = user.getName();
    if (name != null) {
    
    
        System.out.println("User name: " + name);
    }
}

在这个示例中,我们使用了多层的 null 检查来避免空指针异常。代码看起来冗长而且不易读,可读性差,难以理解。

现在,让我们使用 Optional 来优化这段代码:

Optional<User> optionalUser = Optional.ofNullable(getUser());
optionalUser.map(User::getName)
            .ifPresent(name -> System.out.println("User name: " + name));

通过使用 Optional,我们可以将 null 检查和操作合并到一起,使代码更加简洁和易读。

  • 首先,我们使用 Optional.ofNullable 方法来创建一个 Optional 对象,它可以包含一个可能为 null 的值。
  • 然后,我们使用 map 方法来从 Optional 对象中获取用户的名字。最后,我们使用 ifPresent 方法来在名字存在时执行打印操作。

更复杂点儿的示例如下:

// 传统方式
if (user != null) {
    
    
    if (user.getAge() != null) {
    
    
        int age = user.getAge();
        System.out.println("User age: " + age);
    } else {
    
    
        System.out.println("Age not available");
    }
} else {
    
    
    System.out.println("User not found");
}
// 使用 Optional
Optional<User> userOptional = Optional.ofNullable(user);
userOptional.map(User::getAge)
        .ifPresent(age -> System.out.println("User age: " + age))
        .orElse(() -> System.out.println("Age not available"))
        .orElse(() -> System.out.println("User not found"));

常见的Optional方法

  1. 创建Optional对象

    • Optional.of(value): 创建一个包含指定值的Optional对象。如果传入的值为null,会抛出NullPointerException。
    • Optional.ofNullable(value): 创建一个包含指定值的Optional对象,允许传入null值。
    • Optional.empty(): 创建一个空的Optional对象,表示值不存在。
  2. 判断Optional是否包含值

    • isPresent(): 判断Optional对象是否包含值,如果有值返回true,否则返回false。
  3. 获取Optional中的值

    • get(): 获取Optional对象中的值。如果Optional对象为空,会抛出NoSuchElementException异常。建议在调用get()方法之前先使用isPresent()方法进行判断。
  4. 处理Optional对象的值

    • ifPresent(Consumer<? super T> consumer): 如果Optional对象包含值,执行指定的操作。传入的参数是一个Consumer函数接口,用于处理Optional中的值。
  5. 处理Optional对象的值,如果值不存在执行指定的逻辑

    • orElse(T other): 如果Optional对象包含值,返回该值,否则返回指定的默认值。
    • orElseGet(Supplier<? extends T> supplier): 如果Optional对象包含值,返回该值,否则通过Supplier函数接口生成一个默认值并返回。
    • orElseThrow(Supplier<? extends X> exceptionSupplier): 如果Optional对象包含值,返回该值,否则通过Supplier函数接口生成一个异常并抛出。
  6. 对Optional对象的值进行转换

    • map(Function<? super T, ? extends U> mapper): 如果Optional对象包含值,对该值进行转换并返回一个新的Optional对象。
    • flatMap(Function<? super T, Optional<U>> mapper): 如果Optional对象包含值,对该值进行转换并返回一个新的Optional对象。与map不同的是,flatMap方法的转换结果必须是一个Optional对象。
  7. 过滤Optional对象的值

    • filter(Predicate<? super T> predicate): 如果Optional对象包含值,并且该值满足指定的条件,返回包含该值的Optional对象,否则返回一个空的Optional对象。

使用示例

1. 不为空值处理

// 1.无返回值 ifPresent
// 传统方式
if (user.isPresent()) {
    
    
  System.out.println(user.get());
}
// 优化后
user.ifPresent(System.out::println);

// 2.有返回值 map
// 传统方式
User user = .....
if(user != null) {
    
    
  String name = user.getUsername();
  if(name != null) {
    
    
    return name.toUpperCase();
  } else {
    
    
    return null;
  }
} else {
    
    
  return null;
}
// 优化后
user.map(u -> u.getUsername())
    .map(name -> name.toUpperCase())
    .orElse(null);

2. 为空值处理

// 1.为空返回默认值 orElse("Unknown")
// 传统方式
User user = getUser();
if (user != null) {
    
    
    String name = user.getName();
   if(name != null) {
    
    
       return name;
   } else {
    
    
    return "Unknown";
   } 
} else {
    
    
  return "Unknown";
}    
// 优化后    
Optional<User> optionalUser = Optional.ofNullable(getUser());
String name = optionalUser.map(User::getName)
                         .orElse("Unknown");
// 2.为空抛出指定异常 orElseThrow()
// 传统方式
public String getUserName(long userId) {
    
    
    User user = userRepository.findById(userId);
    if (user != null) {
    
    
        return user.getName();
    } else {
    
    
        throw new UserNotFoundException("User not found");
    }
}
// 优化后
public String getUserName(long userId) {
    
    
    Optional<User> userOptional = userRepository.findById(userId);
    return userOptional.map(User::getName).orElseThrow(() -> new UserNotFoundException("User not found"));
}

3. 避免空指针异常

当我们访问一个对象的属性或调用其方法时,经常需要进行空指针检查,以避免出现 NullPointerException。使用 Optional 可以简化这种检查,使得代码更加简洁和安全。

Optional<String> name = Optional.ofNullable(user.getName());
String result = name.orElse("Unknown");

在上述示例中,我们使用 Optional.ofNullable 方法来创建一个 Optional 对象,并通过 orElse 方法设置一个默认值,以防止 name 为空时出现 NullPointerException。

4. 级联操作

Optional<User> optionalUser = Optional.ofNullable(getUser());
Optional<String> optionalEmail = optionalUser.map(User::getEmail);
Optional<String> optionalDomain = optionalEmail.flatMap(this::extractDomain);

在这个示例中,我们使用 mapflatMap 方法来对多个 Optional 进行级联操作,从而避免了多层的 null 检查。

5. 过滤操作

Optional<User> optionalUser = Optional.ofNullable(getUser());
Optional<User> filteredUser = optionalUser.filter(user -> user.getAge() >= 18);

在这个示例中,我们使用 filter 方法来过滤符合条件的用户,只保留年龄大于等于 18 岁的用户。

6. 链式调用

Optional 提供了一系列的操作方法,可以进行链式调用,使得代码更加流畅和易读。

String email = userRepository.findById(userId)
        .flatMap(user -> user.getEmail())
        .orElse("No email found");

在上述示例中,我们通过链式调用的方式,先从 UserRepository 中根据用户ID查询用户对象,然后通过 flatMap 方法获取用户的邮箱。如果邮箱为空,我们使用 orElse 方法设置一个默认值。

7. 复杂场景结合stream api

假设我们有一个 Order 类,它包含一个 items 属性,代表订单中的商品列表。我们需要计算订单的总金额。

传统方式是通过判断订单是否为空以及商品列表是否为空来进行处理:

public BigDecimal calculateTotalAmount(Order order) {
    
    
    if (order != null && order.getItems() != null) {
    
    
        BigDecimal totalAmount = BigDecimal.ZERO;
        for (OrderItem item : order.getItems()) {
    
    
            totalAmount = totalAmount.add(item.getPrice());
        }
        return totalAmount;
    } else {
    
    
        return BigDecimal.ZERO;
    }
}

Optional优化后

public BigDecimal calculateTotalAmount(Order order) {
    
    
    return Optional.ofNullable(order)
            .map(Order::getItems)
            .orElse(Collections.emptyList())
            .stream()
            .map(OrderItem::getPrice)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
}

在上述示例中,我们通过调用 Optional.ofNullable(order) 方法获取一个 Optional 对象,然后使用 map() 方法获取订单的商品列表。如果订单或商品列表为空,我们使用 orElse(Collections.emptyList()) 方法返回一个空的列表。然后,我们使用流式编程的方式计算商品价格的总和。

猜你喜欢

转载自blog.csdn.net/abu935009066/article/details/131669659