Java11 Optional

发布时间 2023-06-11 22:15:55作者: 伊文小哥

简介

public final class Optional<T> {

    private static final Optional<?> EMPTY = new Optional<>();

    private final T value;

    private Optional() {
        this.value = null;
    }
    
    ……
}

Optional<T> 是个容器,在java.util包中用保存类型T的值或者保存null的工具类。Optional<T> 类的引入很好的解决空指针异常

Optional类设计意图:

  • Optional 尽量用作为方法的返回值
  • Optional 清晰的表达返回值是没有值的可能性,并且如果直接返回 null 可能会导致调用端出现空指针异常

构造 Optional<T> 方法

Optional.of(T value)

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

Optional.of(T value)该方法通过一个非 null 的 value 来构造一个 Optional,返回的 Optional 包含 value 这个值

  • 传入的参数一定不能为 null,否则便会抛出 NullPointerException

Optional.empty()

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

Optional.empty()该方法用来构造一个空的 Optional,即该 Optional 中不包含值

Optional.ofNullable(T value)

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

Optional.ofNullable(T value)该方法传入的参数可以为 null 。该方法会判断传入的参数是否为 null,如果为 null 的话,返回 Optional.empty()


Optional<T> 相关方法使用

get()

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

如果 Optional 中有值,就返回该值,否则抛出异常——确保 Optional 内有值才能调用 get() 方法,结合 isPresent() 方法使用

isPresent()

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

如果 Optional 中有值,则返回 true,否者返回 false。常用于检测查询数据返回的实体是否存在。使用实例

public interface UserInfoRepository {
    Optional<UserInfo> getUserInfoById(id);
}

Optional<UserInfo> userInfo = getUserInfoById(id);
if(userInfo.isPresent()){
    UserInfo info = userInfo.get();
}

ifPresent(Consumer<? super T> consumer)

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

如果 Optional 中值存在则使用该值调用 consumer , 否则不做任何事情。使用实例

public void saveUserInfo(String userId,String userName,String alias) {
    UserInfo userInfo = new UserInfo();
    userInfo.setUserId(userId);
    userInfo.setUserName(userName);
    //别名不为null时设置别名——还不如用三元表达式
    Optional.ofNullable(alias).ifPresent(a -> userInfo.setAlias(a));
    
    userInfoRepository.save(userInfo);
}

or(Supplier<? extends Optional<? extends T>> supplier)

public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) {
    Objects.requireNonNull(supplier);
    if (isPresent()) {
        return this;
    } else {
        @SuppressWarnings("unchecked")
        Optional<T> r = (Optional<T>) supplier.get();
        return Objects.requireNonNull(r);
    }
}

如果一个 Optional 包含值,则返回自己;否则返回由参数 supplier 获得的 Optional。使用实例

public interface UserInfoRepository {
    Optional<UserInfo> getUserInfoById(id);
}

public void saveOrUpdateUserInfoName(String userId,String userName) {
    UserInfo userInfo = userInfoRepository.getUserInfoById(id).or(()->Optional.of(new UserInfo(userId,userName))).get();
    userInfo.setUserName(userName);
    userInfoRepository.save(userInfo);
}

orElse(T other)

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

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

public interface UserInfoRepository {
    Optional<UserInfo> getUserInfoById(id);
}

public void saveOrUpdateUserInfoName(String userId,String userName) {
    UserInfo userInfo = userInfoRepository.getUserInfoById(id).orElse(new UserInfo(userId,userName));
    userInfo.setUserName(userName);
    userInfoRepository.save(userInfo);
}

orElseGet(Supplier<? extends T> other)

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

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

public interface UserInfoRepository {
    Optional<UserInfo> getUserInfoById(id);
}

public void saveOrUpdateUserInfoName(String userId,String userName) {
    UserInfo userInfo = userInfoRepository.getUserInfoById(id).orElseGet(()->{
        UserInfo newbie = new UserInfo();
        newbie.setUserId(userId);
        newbie.setUserName(userName);
        newbie.setCreatedAt(LocalDateTime.now());
        newbie.setUpdatedAt(LocalDateTime.now());
        return newbie;
    });
    userInfoRepository.save(userInfo);
}

orElseThrow() 和 orElseThrow(Supplier<? extends X> exceptionSupplier) 

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

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
    if (value != null) {
        return value;
    } else {
        throw exceptionSupplier.get();
    }
}

orElseThrow 方法当 Optional 中有值的时候,返回值;没有值的时候会抛出异常,抛出的异常或者抛出自定义的exceptionSupplier 。使用实例

public interface UserInfoRepository {
    Optional<UserInfo> findUserInfoByUserId(userId);
}


public UserInfo getUserInfo(String userId) {
    Optional<UserInfo> userInfoByUserId = userInfoRepository.findUserInfoByUserId(userId);
    return userInfoByUserId.orElseThrow();
}

public UserInfo getUserInfo(String userId) {
    Optional<UserInfo> userInfoByUserId = userInfoRepository.findUserInfoByUserId(userId);
    return userInfoByUserId.orElseThrow(()->new EntityNotFoundException("userId为 " + userId + " 的用户没有找到"));
}

map(Function<? super T, ? extends U> mapper)

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 作为输入时的输出值。可以多次使用map操作。使用实例

public interface UserInfoRepository {
    Optional<UserAddress> getUserInfoById(userId);
}

public UserAddress getUserAddress(String userId){
    Optional<UserAddress> userAddress = userInfoRepository.getUserInfoById(userId)
        .map(user -> user.getUserAddress());
    
    return userAddress.orElse(new UserAddress());
}

public String getUserPhone(String userId){
    Optional<String> phone = userInfoRepository.getUserInfoById(userId)
        .map(user -> user.getUserPhone())
        .map(phone -> phone.replaceAll("(\\d{3})\\d{4}(\\d{4})","$1****$2"));
    
    return phone.orElse("");
}

flatMap(Function<? super T, ? extends Optional<? extends U>> mapper)

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

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

public interface UserInfoRepository {
    Optional<UserAddress> getUserInfoById(userId);
}


public String getUserPhone(String userId){
    Optional<String> phone = userInfoRepository.getUserInfoById(userId)
        .flatMap(user -> Optional.of(user.getUserPhone()))
        .flatMap(phone -> Optional.of(phone.replaceAll("(\\d{3})\\d{4}(\\d{4})","$1****$2")));
    
    return phone.orElse("");
}

filter(Predicate<? super T> predicate)

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 。使用实例

public interface UserInfoRepository {
    Optional<UserInfo> getUserInfoById(userId);
}

public UserAddress getUserAddress(String userId){
    Optional<UserAddress> userAddress = userInfoRepository.getUserInfoById(userId)
        .filter(user -> user.getStatus() == 1)
        .map(user -> user.getUserAddress());
    
    return userAddress.orElse(new UserAddress());
}

ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)

public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
    if (value != null) {
        action.accept(value);
    } else {
        emptyAction.run();
    }
}

ifPresentOrElse() 方法需要两个参数:一个 action 和一个 emptyAction。如果对象包含值,会执行 action 的动作,否则运行 emptyAction。使用实例

  • 如果在有值的时候执行某个动作,或者只是跟踪是否定义了某个值,那么这个方法非常有用
public interface UserInfoRepository {
    Optional<UserInfo> getUserInfoById(userId);
}

public void updateUserAddress(String userId,Address address){
    userInfoRepository.getUserInfoById(userId)
        .ifPresentOrElse((userInfo) -> {
            userInfo.setAddress(address);
            userInfoRepository.save(userInfo);
        },() -> logger.info("userInfo not found"));
}

 stream()

public Stream<T> stream() {
    if (!isPresent()) {
        return Stream.empty();
    } else {
        return Stream.of(value);
    }
}

通过把实例转换为 Stream 对象, 从而调用 Stream API。如果该 Optional 没有值,它会得到空的 Stream,有值的情况下,返回包含这个值的 Stream。使用实例

public interface UserInfoRepository {
    Optional<UserInfo> getUserInfoById(userId);
}

public class UserInfoServiceImpl{

    @Override
    public List<UserInfo> getUserInfoByIds(List<String> userIds){
        List<UserInfo> collect = ids.stream()
            .map(this::getUserInfoById) // 获得 Stream<Optional<UserInfo>>
            .flatMap(Optional::stream) // Stream 的 flatMap 方法将多个流合成一个流
            .filter(userInfo -> 1 == userInfo.getStatus())
            .collect(Collectors.toList());
    }
    
    @Override
    public Optional<UserInfo> getUserInfoById(userId){
        return userInfoRepository.getUserInfoById(userId);
    }
}

总结

  • Optional 尽量只用作为方法返回值类型
    • 调用了返回值为 Optional 的方法后,一定要做空值检查
  • 不要过度使用 Optional 避免降低代码可读性和性能
  • 不要给 Optional 变量赋值 null,而应该用 Optional.empty() 表达空值

 

参考:https://zhuanlan.zhihu.com/p/128481434 、https://zhuanlan.zhihu.com/p/40966718