Java Optional 学习与使用

前言

最近在使用SpringBoot data JPA相关接口的时候发现,许多接口的返回类型都从传统的泛型<T>转化为了Optional<T>,而且我们所常用的List等也用了迭代器Iterable进行代替,其中原因是很值得我们去深究的,使用了此类接口有何优势,为何要替代,此类接口的实现方法等

类的方法

从jdk源码中我们可以很快速找到Optional的相关方法:

方法描述
empty()返回空的 Optional 实例。
of(T)返回一个指定非null值的Optional。
ofNullable(T)如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。
get()返回包含的对象
isPresent()如果值存在则方法会返回true,否则返回 false。
isEmpty()判空
ifPresent(Consumer<? super T> action)如果值存在则使用该值调用 consumer , 否则不做任何事情。
ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)ifPresentOrElse 方法的改进就是有了 else,接受两个参数 Consumer 和 Runnable.ifPresentOrElse 方法的用途是,如果一个 Optional 包含值,则对其包含的值调用函数 action,即 action.accept(value),这与 ifPresent 一致;与 ifPresent 方法的区别在于,ifPresentOrElse 还有第二个参数 emptyAction —— 如果 Optional 不包含值,那么 ifPresentOrElse 便会调用 emptyAction,即 emptyAction.run()。
filter(Predicate<? super T> predicate)如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional。
flatMap(Function<? super T, ? extends Optional<? extends U>> mapper)如果值存在,返回基于Optional包含的映射方法的值,否则返回一个空的Optional
or(Supplier<? extends Optional<? extends T>> supplier)
Stream stream()stream 方法的作用就是将 Optional 转为一个 Stream,如果该 Optional 中包含值,那么就返回包含这个值的 Stream,否则返回一个空的 Stream(Stream.empty())。
orElse(T other)如果值存在,返回 Optional 指定的值,否则返回一个预设的值。
orElseGet(Supplier<? extends T> supplier)如果存在该值,返回值, 否则触发 other,并返回 other 调用的结果。
orElseThrow(Supplier<? extends X> exceptionSupplier)如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常
equals(Object obj)判等
hashCode()返回存在值的哈希码,如果值不存在 返回 0。
toString()

首先我们看看这个Optional的介绍:
A container object which may or may not contain a non-{@code null} value. If a value is present, {@code isPresent()} returns {@code true}. If no value is present, the object is considered <i>empty</i> and {@code isPresent()} returns {@code false}.
翻译过来,Optional类就是一个允许object为空的容器,提供了isPresent来判断是否为空。

PS:jdk源码屎山名不虚传,翻都不知道怎么翻,不如直接看实战吧!

实际中的使用

简单的创建Optionnal实例

import java.util.Optional;
 
public class Java8Tester {
   public static void main(String args[]){
   
      Java8Tester java8Tester = new Java8Tester();
      Integer value1 = null;
      Integer value2 = new Integer(10);
        
      // Optional.ofNullable - 允许传递为 null 参数
      Optional<Integer> a = Optional.ofNullable(value1);
        
      // Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException
      Optional<Integer> b = Optional.of(value2);
      System.out.println(java8Tester.sum(a,b));
   }
    
   public Integer sum(Optional<Integer> a, Optional<Integer> b){
    
      // Optional.isPresent - 判断值是否存在
        
      System.out.println("第一个参数值存在: " + a.isPresent());
      System.out.println("第二个参数值存在: " + b.isPresent());
        
      // Optional.orElse - 如果值存在,返回它,否则返回默认值
      Integer value1 = a.orElse(new Integer(0));
        
      //Optional.get - 获取值,值需要存在
      Integer value2 = b.get();
      return value1 + value2;
   }
}

上面的例子主要是对一些简单,常用的方法进行实操,
有几个点需要注意下

判空isPresent()函数

我们可以看到ifPresent()函数与其非常相似,但两者的区别在于,ifPresent()方法出了执行判空检查,还会接收一个
consumer参数,若不为空,则执行其后的Lambda表达式
例如:

Optional<Role> role = roleRepository.findById(roleid);
        role.ifPresent(value -> map.put("role", value));

这样可以让代码看起来更加简洁好看

orElse()orElseGet()

orElse()的作用代码中很明显了,简单的来看orElseGet()会执行作为参数传入的 Supplier(供应者) 函数式接口,并将返回其执行结果
例如:

主体测试函数:

private User createNewUser() {
    logger.debug("Creating New User");
    return new User("extra@gmail.com", "1234");
}
当传入对象为空时,两者并无差异
@Test
public void givenEmptyValue_whenCompare_thenOk() {
    User user = null
    logger.debug("Using orElse");
    User result = Optional.ofNullable(user).orElse(createNewUser());
    logger.debug("Using orElseGet");
    User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
}

输出:

Using orElse
Creating New User
Using orElseGet
Creating New User
当传入的对象不为空时:
@Test
public void givenPresentValue_whenCompare_thenOk() {
    User user = new User("john@gmail.com", "1234");
    logger.info("Using orElse");
    User result = Optional.ofNullable(user).orElse(createNewUser());
    logger.info("Using orElseGet");
    User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
}

输出:

Using orElse
Creating New User
Using orElseGet

这个示例中,两个 Optional 对象都包含非空值,两个方法都会返回对应的非空值。不过,orElse() 方法仍然创建了 User 对象。与之相反,orElseGet() 方法不创建 User 对象

也就是说,orElseGet()方法不会对堆进行多一次的访问,减少了一次IO操作,这在大型高并发系统中是至关重要的

转化值

转化值,顾名思义就是转化Optional的值

map()方法

@Test
    public void createOptionalExample(){
            User user = new User("anna@gmail.com", "1234");
            //::关键字是可以访问类的构造方法,对象方法,静态方法
            String email = Optional.of(user).map(User::getEmail).orElse("default@gmail.com");
        System.out.println(email.equals(user.getEmail()));
            assertEquals(email, user.getEmail());
    }

我们可以看到,map函数主要是将user的值转化为了String,即User中的email属性,若取到的对象为空,则取到的email值为“default@gmail.com”。

flatMap()方法

map()相比,flatMap()方法主要是可以接触包装,比如如果我们的getter()函数是这样:

public class User {    
    private String position;

    public Optional<String> getPosition() {
        return Optional.ofNullable(position);
    }

    //...
}

其返回的是封装好的Optional,flatMap()则是可以直接接触包装,并将获取到的String值返回。这因为flatMap在参数不为null的情况下,返回的是泛型T,所以就可以直接使用get对应的字段、

  • map 在执行完 Function 的操作后, 把新元素包装在 Optional 中返回,
  • flatMap 在执行完 Function 的操作后, 结果直接就是 Optional<新的元素类型>, 无需重新包装

过滤值

filter()方法

接收一个Predicate 参数,返回测试结果为 true 的值。如果测试结果为 false,会返回一个空的 Optional。来看一个根据基本的电子邮箱验证来决定接受或拒绝 User(用户) 的示例:

@Test
public void whenFilter_thenOk() {
    User user = new User("anna@gmail.com", "1234");
    Optional<User> result = Optional.ofNullable(user)
      .filter(u -> u.getEmail() != null && u.getEmail().contains("@"));

    assertTrue(result.isPresent());
}

如果通过了->后的测试,则result非空。

主要作用

目前Optional最常用作返回的类型,在获取到这个类型的实例后,如果它有值,你可以取得这个值,否则可以进行一些替代行为。极大的减少代码中的 NullPointerExceptions,虽然还不能完全消除这些异常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值