Optional的理解与使用

本文详细介绍了Java8中Optional类的特性和使用方法,包括如何避免空指针异常,最佳实践,构造函数及常用函数介绍。

从 Java 8 引入的一个很有趣的特性是 Optional 类。Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException) —— 每个 Java 程序员都非常了解的异常。

本质上,这是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空。

Optional 是 Java 实现函数式编程的强劲一步,并且帮助在范式中实现。但是 Optional 的意义显然不止于此。

我们从一个简单的用例开始。在 Java 8 之前,任何访问对象方法或属性的调用都可能导致 NullPointerException

String province = user.getAddress().getCountry().toUpperCase();

在这个小示例中,如果我们需要确保不触发异常,就得在访问每一个值之前对其进行明确地检查:

public interface UserDao{
  User getUserById(String id);
}
public String getUsername(String id){
	User user = getUserById(id);
	if (user != null) {
    	String username = user.getUsername();
    	return username;
	}
}

使用 Optional 进行优化:

public interface UserDao{
  User getUserById(String id);
}
public String getUsername(String id){
	Optional<User> user = Optional.ofNullable(getUserById(id));
	if (user.isPresent()) {
    	String username = user.get().getUsername();
    	return username;
	}
}

好像看着代码是优美了点哟

—— 但是事实上这与之前判断 null 值的代码没有本质的区别,反而用 Optional 去封装 value,增加了代码量。所以我们来看看 Optional 还提供了哪些方法,让我们更好的(以正确的姿势)使用 Optional。

Optional最佳实践

首先我们先上一张图,来简述一下Optional的使用时机

在这里插入图片描述

  • 避免使用Optional.isPresent()来检查实例是否存在,因为这种方式和null != obj没有区别。

  • 避免使用Optional.get()方式来获取实例对象,因为使用前需要使用Optional.isPresent()来检查实例是否存在,否则会出现NoSuchElementException异常问题。

  • 尽量使用orElse()orElseGet()orElseThrow()获得结果

    orElse(T other) 存在即返回,无则提供默认值

    return user.orElse(null);  //而不是 return user.isPresent() ? user.get() : null;
    return user.orElse(UNKNOWN_USER);
    

    这里要说明一下的是orElse(…)是急切计算,意味着类似下面代码:

    Optional<Dog> optionalDog = fetchOptionalDog();
    optionalDog
     .map(this::printUserAndReturnUser)
     .orElse(this::printVoidAndReturnUser)
    

    如果值存在则将执行两个方法,如果值不存在,则仅执行最后一个方法。为了处理这些情况,我们可以使用方法orElseGe(),它将supplier 作为参数,并且是惰性计算的。

    orElseGet(Supplier<? extends T> other) 存在即返回, 无则由函数来产生

    return user.orElseGet(() -> fetchAUserFromDatabase());  //而不要 return user.isPresent() ? user: fetchAUserFromDatabase();
    
  • 尽量使用ifPresent(Consumer<? super T> consumer)直接对数据进行操作

    ifPresent() 存在才对它做点什么

    user.ifPresent(System.out::println);
    
    //而不要下边那样
    if (user.isPresent()) {
      System.out.println(user.get());
    }
    
  • 避免使用Optional作为类或者实例的属性,而应该在返回值中用来包装返回实例对象。

  • 避免使用Optional作为方法的参数。

  • 不要将null赋给Optional。

  • 只有当结果不确定时,使用Optional作为返回类型,从某种意义上讲,这是使用Optional的唯一好地方,用java官方的话讲就是:我们的目的是为库方法的返回类型提供一种有限的机制,其中需要一种明确的方式来表示“无结果”,并且对于这样的方法使用null 绝对可能导致错误。

  • 不要害怕使用mapfilter,有一些值得遵循的一般开发实践称为SLA-p:Single Layer of Abstraction字母的第一个大写。

下面是需要被重构代码到重构的代码

示例一

Dog dog = fetchSomeVaraible();
String dogString = dogToString(dog);
public String dogToString(Dog dog){
 if(dog == null){
   return "DOG'd name is : " + dog.getName();
 } else { 
   return "CAT";
 }
}
//上面代码重构到下面代码
Optional<Dog> dog = fetchDogIfExists();
String dogsName = dog
 .map(this::convertToDog)
 .orElseGet(this::convertToCat)

public void convertToDog(Dog dog){
   return "DOG'd name is : " + dog.getName();
}

public void convertToCat(){
   return "CAT";
}

示例二

Dog dog = fetchDog();
if(optionalDog != null && optionalDog.isBigDog()){
  doBlaBlaBla(optionalDog);
}
//上面代码重构到下面代码
Optional<Dog> optionalDog = fetchOptionalDog();
optionalDog
 .filter(Dog::isBigDog)
 .ifPresent(this::doBlaBlaBla)
  • 使所有表达式成为单行lambda。这是更普遍的规则,我认为也应该应用于流。但这篇文章是关于optional 。使用Optional 重要点是记住等式左边和右边一样重要。

    这里举个例子

    Optional
     .ofNullable(someVariable)
     .map(variable -> {
       try{
          return someREpozitory.findById(variable.getIdOfOtherObject());
       } catch (IOException e){
         LOGGER.error(e); 
         throw new RuntimeException(e); 
       }})
     .filter(variable -> { 
       if(variable.getSomeField1() != null){
         return true;
       } else if(variable.getSomeField2() != null){
         return false;   
       } else { 
         return true;
       }
      })
     .map((variable -> {
       try{
          return jsonMapper.toJson(variable);
       } catch (IOException e){
         LOGGER.error(e); 
         throw new RuntimeException(e); 
       }}))
     .map(String::trim)
     .orElseThrow(() -> new RuntimeException("something went horribly wrong."))
    

    上面那么冗长代码块可以使用方法替代:

    Optional
     .ofNullable(someVariable)
     .map(this::findOtherObject)
     .filter(this::isThisOtherObjectStale)
     .map(this::convertToJson)
     .map(String::trim)
     .orElseThrow(() -> new RuntimeException("something went horribly wrong."));
    

Optional的构造函数

Optional 的三种构造方式:Optional.of(T value)Optional.ofNullable(T value) 和明确的 Optional.empty()

Optional.of(T value)

public static <T> Optional<T> of(T value) {
    return new Optional<>(value);
}

该方法通过一个非 null 的 value 来构造一个 Optional,返回的 Optional 包含了 value 这个值。对于该方法,传入的参数一定不能为 null,否则便会抛出 NullPointerException。。

Optional.ofNullable(T value)

public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}

该方法以一种智能的,宽容的方式来构造一个 Optional 实例。来者不拒,传 null 值就得到 Optional.empty(),非 null 值就调用 Optional.of(T value)。

Optional.empty()

public static<T> Optional<T> empty() {
    @SuppressWarnings("unchecked")
    Optional<T> t = (Optional<T>) EMPTY;
    return t;
}

该方法用来构造一个空的 Optional,即该 Optional 中不包含值。

Optional的常用函数

isPresent

源码

public boolean isPresent() {
    return value != null;
}

如果 Optional 中值存在返回 true,否则返回 false。不经常使用。

示例代码

Optional<User> user = Optional.ofNullable(getUserById(id));
if(user.isPresent()){
  	System.out.println("Username is: " + user.get().getUsername());
}else {
  	System.out.println("Username is unknow!");
}

ifPresent

源码

public void ifPresent(Consumer<? super T> consumer) {
    if (value != null)
        consumer.accept(value);
}

如果 Optional 中有值,则对该值调用 consumer.accept,否则什么也不做。

示例代码

Optional<User> user = Optional.ofNullable(getUserById(id));
user.ifPresent(u -> System.out.println("Username is: " + u.getUsername()));

get

源码

public T get() {
    if (value == null) {
        throw new NoSuchElementException("No value present");
    }
    return value;
}

如果Optional有值则将其返回,否则抛出NoSuchElementException。不经常使用。

示例

Optional<User> user = Optional.ofNullable(getUserById(id));
if(user.isPresent()){
  	System.out.println("Username is: " + user.get().getUsername());
}else {
  	System.out.println("Username is unknow!");
}

orElse

源码

public T orElse(T other) {
        return value != null ? value : other;
}

如果 Optional 中有值则将其返回,否则返回 orElse 方法传入的参数。

示例

User user = Optional
        .ofNullable(getUserById(id))
        .orElse(new User(0, "Unknown"));
        
System.out.println("Username is: " + user.getUsername());

orElseGet

源码

public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
}

orElseGetorElse 方法的区别在于,orElseGet 方法传入的参数为一个 Supplier 接口的实现 —— 当 Optional 中有值的时候,返回值;当 Optional 中没有值的时候,返回从该 Supplier 获得的值。

示例

User user = Optional
        .ofNullable(getUserById(id))
        .orElseGet(() -> new User(0, "Unknown"));
        
System.out.println("Username is: " + user.getUsername());

orElseThrow

源码

public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
}

orElseThroworElse 方法的区别在于,orElseThrow方法当 Optional 中有值的时候,返回值;没有值的时候会抛出异常,抛出的异常由传入的 exceptionSupplier 提供。

示例

User user = Optional
        .ofNullable(getUserById(id))
        .orElseThrow(() -> new EntityNotFoundException("id 为 " + id + " 的用户没有找到"));

举一个 orElseThrow 的用途:在 SpringMVC 的控制器中,我们可以配置统一处理各种异常。查询某个实体时,如果数据库中有对应的记录便返回该记录,否则就可以抛出 EntityNotFoundException ,处理 EntityNotFoundException 的方法中我们就给客户端返回Http 状态码 404 和异常对应的信息 —— orElseThrow 完美的适用于这种场景。

@RequestMapping("/{id}")
public SysUser getSysUser(@PathVariable Integer id) {
    Optional<SysUser> user = userService.getSysUserById(id);
    return user.orElseThrow(() -> new EntityNotFoundException("id 为 " + id + " 的用户不存在"));
}

@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<String> handleException(EntityNotFoundException ex) {
    return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
}

map

源码

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Optional.ofNullable(mapper.apply(value));
    }
}

如果当前 Optional 为 Optional.empty,则依旧返回 Optional.empty;否则返回一个新的 Optional,该 Optional 包含的是:函数 mapper 在以 value 作为输入时的输出值。

示例

Optional<String> username = Optional
        .ofNullable(getUserById(id))
        .map(user -> user.getUsername());
        
System.out.println("Username is: " + username.orElse("Unknown"));

//我们可以多次使用map操作
Optional<String> username = Optional
        .ofNullable(getUserById(id))
        .map(user -> user.getUsername())
        .map(name -> name.toLowerCase())
        .map(name -> name.replace('_', ' '));
        
System.out.println("Username is: " + username.orElse("Unknown"));

flatMap

源码

public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Objects.requireNonNull(mapper.apply(value));
    }
}

flatMap 方法与 map 方法的区别在于:map 方法参数中的函数 mapper 输出的是值,然后 map 方法会使用 Optional.ofNullable 将其包装为 Optional;而 flatMap 要求参数中的函数 mapper 输出的就是 Optional。

示例

Optional<String> username = Optional
        .ofNullable(getUserById(id))
        .flatMap(user -> Optional.of(user.getUsername()))
        .flatMap(name -> Optional.of(name.toLowerCase()));
        
System.out.println("Username is: " + username.orElse("Unknown"));

filter

源码

public Optional<T> filter(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    if (!isPresent())
        return this;
    else
        return predicate.test(value) ? this : empty();
}

filter 方法接受一个 Predicate 来对 Optional 中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个 Optional;否则返回 Optional.empty。

示例

Optional<String> username = Optional
        .ofNullable(getUserById(id))
        .filter(user -> user.getId() < 10)
        .map(user -> user.getUsername());
        
System.out.println("Username is: " + username.orElse("Unknown"));

Optional 场景演示

package crazy;

import java.util.Optional;

class Company {
    private String name;
    private Optional<Office> office;

    public Company(String name, Optional<Office> office) {
        this.name = name;
        this.office = office;
    }

    public String getName() {
        return name;
    }

    public Optional<Office> getOffice() {
        return office;
    }
}

class Office {
    private String id;
    private Optional<Address> address;

    public Office(String id, Optional<Address> address) {
        this.id = id;
        this.address = address;
    }

    public String getId() {
        return id;
    }

    public Optional<Address> getAddress() {
        return address;
    }
}

class Address {
    private Optional<String> street;
    private Optional<String> city;

    public Address(Optional<String> street, Optional<String> city) {
        this.street = street;
        this.city = city;
    }

    public Optional<String> getStreet() {
        return street;
    }

    public Optional<String> getCity() {
        return city;
    }
}

public class OptionalDemo1 {

    public static void main(String[] args) {
        Optional<Address> address1 = Optional.of(new Address(Optional.ofNullable(null), Optional.of("New York")));
        Optional<Office> office1 = Optional.of(new Office("OF1", address1));
        Optional<Company> company1 = Optional.of(new Company("Door Never Closed", office1));

        // What is the street address of company1?
        // In which city company1 is located?
        Optional<Office> maybeOffice = company1.flatMap(Company::getOffice);
        Optional<Address> maybeAddress = office1.flatMap(Office::getAddress);
        Optional<String> maybeStreet = address1.flatMap(Address::getStreet);

        maybeStreet.ifPresent(System.out::println);
        if (maybeStreet.isPresent()) {
            System.out.println(maybeStreet.get());
        } else {
            System.out.println("Street not found.");
        }

        // shorter way
        String city = company1.flatMap(Company::getOffice)
                .flatMap(Office::getAddress)
                .flatMap(Address::getStreet)
                .orElse("City is not found.");

        System.out.println("City: " + city);

         // only print if city is not null
        company1.flatMap(Company::getOffice)
                .flatMap(Office::getAddress)
                .flatMap(Address::getCity)
                .ifPresent(System.out::println);

    }
}

附录:Optional的源码

/*
 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package java.util;

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * A container object which may or may not contain a non-null value.
 * If a value is present, {@code isPresent()} will return {@code true} and
 * {@code get()} will return the value.
 *
 * <p>Additional methods that depend on the presence or absence of a contained
 * value are provided, such as {@link #orElse(java.lang.Object) orElse()}
 * (return a default value if value not present) and
 * {@link #ifPresent(java.util.function.Consumer) ifPresent()} (execute a block
 * of code if the value is present).
 *
 * <p>This is a <a href="../lang/doc-files/ValueBased.html">value-based</a>
 * class; use of identity-sensitive operations (including reference equality
 * ({@code ==}), identity hash code, or synchronization) on instances of
 * {@code Optional} may have unpredictable results and should be avoided.
 *
 * @since 1.8
 */
public final class Optional<T> {
    /**
     * Common instance for {@code empty()}.
     */
    // Null指针的封装
    private static final Optional<?> EMPTY = new Optional<>();

    /**
     * If non-null, the value; if null, indicates no value is present
     */
  	// 内部包含的值对象
    private final T value;

    /**
     * Constructs an empty instance.
     *
     * @implNote Generally only one empty instance, {@link Optional#EMPTY},
     * should exist per VM.
     */
  	// 无参构造函数
    private Optional() {
        this.value = null;
    }

    /**
     * Returns an empty {@code Optional} instance.  No value is present for this
     * Optional.
     *
     * @apiNote Though it may be tempting to do so, avoid testing if an object
     * is empty by comparing with {@code ==} against instances returned by
     * {@code Option.empty()}. There is no guarantee that it is a singleton.
     * Instead, use {@link #isPresent()}.
     *
     * @param <T> Type of the non-existent value
     * @return an empty {@code Optional}
     */
  	// 返回EMPTY对象
    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }

    /**
     * Constructs an instance with the value present.
     *
     * @param value the non-null value to be present
     * @throws NullPointerException if value is null
     */
  	// 有参构造函数,但是value不能为null,否则会报NPE
    private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }

    /**
     * Returns an {@code Optional} with the specified present non-null value.
     *
     * @param <T> the class of the value
     * @param value the value to be present, which must be non-null
     * @return an {@code Optional} with the value present
     * @throws NullPointerException if value is null
     */
  	//静态工厂方法,但是value不能为null,否则会报NPE
    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }

    /**
     * Returns an {@code Optional} describing the specified value, if non-null,
     * otherwise returns an empty {@code Optional}.
     *
     * @param <T> the class of the value
     * @param value the possibly-null value to describe
     * @return an {@code Optional} with a present value if the specified value
     * is non-null, otherwise an empty {@code Optional}
     */
  	//静态工厂方法,value可以为null
    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

    /**
     * If a value is present in this {@code Optional}, returns the value,
     * otherwise throws {@code NoSuchElementException}.
     *
     * @return the non-null value held by this {@code Optional}
     * @throws NoSuchElementException if there is no value present
     *
     * @see Optional#isPresent()
     */
  	//获取value,但是value为null,会报NoSuchElementException
    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

    /**
     * Return {@code true} if there is a value present, otherwise {@code false}.
     *
     * @return {@code true} if there is a value present, otherwise {@code false}
     */
  	//返回value是否为null
    public boolean isPresent() {
        return value != null;
    }

    /**
     * If a value is present, invoke the specified consumer with the value,
     * otherwise do nothing.
     *
     * @param consumer block to be executed if a value is present
     * @throws NullPointerException if value is present and {@code consumer} is
     * null
     */
  	//如果value不为null,则执行consumer式的函数;如果为null则什么都不做
    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }

    /**
     * If a value is present, and the value matches the given predicate,
     * return an {@code Optional} describing the value, otherwise return an
     * empty {@code Optional}.
     *
     * @param predicate a predicate to apply to the value, if present
     * @return an {@code Optional} describing the value of this {@code Optional}
     * if a value is present and the value matches the given predicate,
     * otherwise an empty {@code Optional}
     * @throws NullPointerException if the predicate is null
     */
  	//过滤,如果value不为null,则根据条件过滤;如果为null则什么都不做
    public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }

    /**
     * If a value is present, apply the provided mapping function to it,
     * and if the result is non-null, return an {@code Optional} describing the
     * result.  Otherwise return an empty {@code Optional}.
     *
     * @apiNote This method supports post-processing on optional values, without
     * the need to explicitly check for a return status.  For example, the
     * following code traverses a stream of file names, selects one that has
     * not yet been processed, and then opens that file, returning an
     * {@code Optional<FileInputStream>}:
     *
     * <pre>{@code
     *     Optional<FileInputStream> fis =
     *         names.stream().filter(name -> !isProcessedYet(name))
     *                       .findFirst()
     *                       .map(name -> new FileInputStream(name));
     * }</pre>
     *
     * Here, {@code findFirst} returns an {@code Optional<String>}, and then
     * {@code map} returns an {@code Optional<FileInputStream>} for the desired
     * file if one exists.
     *
     * @param <U> The type of the result of the mapping function
     * @param mapper a mapping function to apply to the value, if present
     * @return an {@code Optional} describing the result of applying a mapping
     * function to the value of this {@code Optional}, if a value is present,
     * otherwise an empty {@code Optional}
     * @throws NullPointerException if the mapping function is null
     */
  	//转换,在其外面封装Optional,如果value不为null,则map转换;如果为null则什么都不做
    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }

    /**
     * If a value is present, apply the provided {@code Optional}-bearing
     * mapping function to it, return that result, otherwise return an empty
     * {@code Optional}.  This method is similar to {@link #map(Function)},
     * but the provided mapper is one whose result is already an {@code Optional},
     * and if invoked, {@code flatMap} does not wrap it with an additional
     * {@code Optional}.
     *
     * @param <U> The type parameter to the {@code Optional} returned by
     * @param mapper a mapping function to apply to the value, if present
     *           the mapping function
     * @return the result of applying an {@code Optional}-bearing mapping
     * function to the value of this {@code Optional}, if a value is present,
     * otherwise an empty {@code Optional}
     * @throws NullPointerException if the mapping function is null or returns
     * a null result
     */
  	//转换,如果value不为null,则map转换;如果为null则什么都不做
    public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }

    /**
     * Return the value if present, otherwise return {@code other}.
     *
     * @param other the value to be returned if there is no value present, may
     * be null
     * @return the value, if present, otherwise {@code other}
     */
  	//如果 value 中有值则将其返回,否则返回 orElse 方法传入的参数
    public T orElse(T other) {
        return value != null ? value : other;
    }

    /**
     * Return the value if present, otherwise invoke {@code other} and return
     * the result of that invocation.
     *
     * @param other a {@code Supplier} whose result is returned if no value
     * is present
     * @return the value if present otherwise the result of {@code other.get()}
     * @throws NullPointerException if value is not present and {@code other} is
     * null
     */
  	//orElseGet 与 orElse 方法的区别在于,orElseGet 方法传入的参数为一个 Supplier 接口的实现 —— 当 Optional 中有值的时候,返回值;
    //当 Optional 中没有值的时候,返回从该 Supplier 获得的值
    public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }

    /**
     * Return the contained value, if present, otherwise throw an exception
     * to be created by the provided supplier.
     *
     * @apiNote A method reference to the exception constructor with an empty
     * argument list can be used as the supplier. For example,
     * {@code IllegalStateException::new}
     *
     * @param <X> Type of the exception to be thrown
     * @param exceptionSupplier The supplier which will return the exception to
     * be thrown
     * @return the present value
     * @throws X if there is no value present
     * @throws NullPointerException if no value is present and
     * {@code exceptionSupplier} is null
     */
  	//orElseThrow 与 orElse 方法的区别在于,orElseThrow 方法当 Optional 中有值的时候,返回值;没有值的时候会抛出异常,抛出的异常由传入的 exceptionSupplier 提供
    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }

    /**
     * Indicates whether some other object is "equal to" this Optional. The
     * other object is considered equal if:
     * <ul>
     * <li>it is also an {@code Optional} and;
     * <li>both instances have no value present or;
     * <li>the present values are "equal to" each other via {@code equals()}.
     * </ul>
     *
     * @param obj an object to be tested for equality
     * @return {code true} if the other object is "equal to" this object
     * otherwise {@code false}
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (!(obj instanceof Optional)) {
            return false;
        }

        Optional<?> other = (Optional<?>) obj;
        return Objects.equals(value, other.value);
    }

    /**
     * Returns the hash code value of the present value, if any, or 0 (zero) if
     * no value is present.
     *
     * @return hash code value of the present value or 0 if no value is present
     */
    @Override
    public int hashCode() {
        return Objects.hashCode(value);
    }

    /**
     * Returns a non-empty string representation of this Optional suitable for
     * debugging. The exact presentation format is unspecified and may vary
     * between implementations and versions.
     *
     * @implSpec If a value is present the result must include its string
     * representation in the result. Empty and present Optionals must be
     * unambiguously differentiable.
     *
     * @return the string representation of this instance
     */
    @Override
    public String toString() {
        return value != null
            ? String.format("Optional[%s]", value)
            : "Optional.empty";
    }
}
<think>好的,用户现在的问题是关于如何使用Optional来改进之前的代码。首先,我需要回顾用户提供的原始代码,看看哪里可能存在空指针风险,然后思考如何用Optional来处理这些情况。 用户之前的代码中,主要问题包括:requestDto.getCompetitionPersonnelList()可能为null,personnel.getRedUserInfo()或personnel.getBlueUserInfo()可能为null,以及userInfo可能为null导致的NPE。现在用户希望使用Optional来优化这些部分。 首先,处理requestDto.getCompetitionPersonnelList()可能为null的情况。可以用Optional.ofNullable包装这个列表,然后转换为流。例如,Optional.ofNullable(requestDto.getCompetitionPersonnelList()).stream().flatMap(Collection::stream),这样如果列表为null,流会是空的,避免NPE。 接下来,处理personnel.getRedUserInfo()和personnel.getBlueUserInfo()可能为null的情况。可以用Optional.ofNullable来包装这两个方法的返回值,然后过滤掉null,再转换为流。例如,Stream.of(personnel.getRedUserInfo(), personnel.getBlueUserInfo()).flatMap(userInfo -> Optional.ofNullable(userInfo).stream()),这样可以过滤掉null的userInfo。 然后,在map操作中,确保userInfo不为null,因为已经用Optional过滤过了,所以可以安全地访问其方法。 另外,需要确保整个流操作的完整性,包括终端操作collect(Collectors.toList()),避免语法错误。 还要考虑业务逻辑的完整性,确保CompetitionPo的属性被正确赋值,这部分可能需要用户根据实际需求填充,但Optional主要用于处理空值,不影响业务属性的赋值。 总结下来,使用Optional的关键点在于:用Optional.ofNullable包装可能为null的值,转换为流,过滤掉null元素,确保后续操作的安全性。同时,保持流操作的连贯性,正确使用flatMap和map,最后用终端操作收集结果。 现在需要将这些思路整理成步骤,解释如何用Optional改进代码,并指出每个步骤解决的问题。同时,需要生成相关的问题,帮助用户进一步理解Optional使用场景和注意事项。 </think>在原始代码中使用 `Optional` 可以有效避免空指针异常(NPE),并让空值处理逻辑更优雅。以下是结合 `Optional` 的优化方案及具体分析: ### **1. 处理 `requestDto.getCompetitionPersonnelList()` 可能为 `null`** 若 `requestDto.getCompetitionPersonnelList()` 返回 `null`,直接调用 `stream()` 会抛出 `NPE`。使用 `Optional` 包装列表,若为 `null` 则返回空流: ```java List<CompetitionPo> competitionPoList = Optional.ofNullable(requestDto.getCompetitionPersonnelList()) .orElse(Collections.emptyList()) // 若为null,替换为空列表 .stream() // 后续流操作... ``` 或直接通过 `flatMap` 转换为流(更简洁): ```java List<CompetitionPo> competitionPoList = Optional.ofNullable(requestDto.getCompetitionPersonnelList()) .stream() // 将Optional转换为包含列表的流(可能为空) .flatMap(Collection::stream) // 展开列表为元素流(若原列表为null,此流为空) // 后续流操作... ``` ### **2. 处理 `redUserInfo` 和 `blueUserInfo` 可能为 `null`** `personnel.getRedUserInfo()` 或 `personnel.getBlueUserInfo()` 可能返回 `null`,需用 `Optional` 过滤无效值: ```java .flatMap(personnel -> Stream.of( Optional.ofNullable(personnel.getRedUserInfo()), // 包装可能为null的redUserInfo Optional.ofNullable(personnel.getBlueUserInfo()) // 包装可能为null的blueUserInfo ) .flatMap(Optional::stream) // 仅保留非null的userInfo(若为null则流为空) .map(userInfo -> { CompetitionPo competitionPo = new CompetitionPo(); // 安全访问userInfo的方法(已过滤null) competitionPo.setUserId(userInfo.getUserId()); competitionPo.setUserName(userInfo.getUserName()); return competitionPo; }) ) ``` ### **3. 完整优化后的代码示例** ```java List<CompetitionPo> competitionPoList = Optional.ofNullable(requestDto.getCompetitionPersonnelList()) .stream() .flatMap(Collection::stream) // 处理requestDto.getCompetitionPersonnelList()为null或空的情况 .flatMap(personnel -> Stream.of( Optional.ofNullable(personnel.getRedUserInfo()), Optional.ofNullable(personnel.getBlueUserInfo()) ) .flatMap(Optional::stream) // 过滤redUserInfo/blueUserInfo为null的情况 .map(userInfo -> { CompetitionPo competitionPo = new CompetitionPo(); // 填充业务属性(userInfo已确保非null) competitionPo.setUserId(userInfo.getUserId()); competitionPo.setCompetitionId(requestDto.getCompetitionId()); // 其他必要属性... return competitionPo; }) ) .collect(Collectors.toList()); // 终端操作,收集结果 ``` ### **关键优势** - **空值安全**:通过 `Optional.ofNullable` 和 `Optional::stream` 过滤所有可能的 `null`,避免 `NPE`。 - **代码简洁**:无需显式的 `if (xxx != null)` 判断,逻辑更清晰。 - **链式操作**:结合流的链式调用,保持代码的函数式风格。 ### **注意事项** - **避免过度包装**:若明确某个方法**不会返回 `null`**(如JPA查询强制返回非空对象),无需用 `Optional` 包装,否则会增加冗余。 - **业务逻辑完整性**:`Optional` 仅处理空值,仍需确保 `CompetitionPo` 的业务属性(如 `userId`、`competitionId`)被正确赋值。 - **性能影响**:`Optional` 本身无显著性能损耗,但多层 `flatMap` 可能影响可读性,需权衡简洁性和可维护性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值