是时候啦!用Optional取代null!

这是我参与更文挑战的第10天

基于Java8的介绍,在Java9中对 Optional 的增强

参考文档:www.wdbyte.com/2019/11/jdk…

1.Optional介绍

Optional是Jdk1.8提供的一个新类,希望可以通过该类的引入解决令人烦躁的null判断问题,非常好用。

该类有点类似于包装类,将要操作的Java类封装到该类的对象里面,同时将一些常用的判断逻辑封装为成员方法,结合lambda语法,实现比较优雅的链式调用。

  1. 构建API:构建一个Optional对象;方法有:empty()of()ofNullable()

  2. 获取API:获取Optional对象里包装的值;方法有:get()orElse()orElseGet()orElseThrow()

  3. 转换API:将Optional对象里包装的值转换成一个新的值;方法有:map()flatMap()

  4. 判断API:对Optional对象里包装的值做一些逻辑判断;方法有:filter()isPresent()ifPresent()

image-20210609204728510

2.构建类

简单准备一个Java类

package com.hanpang.model;
import lombok.*;
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class User {
    private String username;
    private String sex;
    private Integer age;
}
复制代码

创建Optional对象方式:

 @Test
public void buildOptionalTest(){
    // 1.构建一个value为null的optional对象;
    Optional<User> userEmptyOpt = Optional.empty();//“空”Optional对象

    // 2.构建一个value不可以为null的optional对象,如果of()的入参为null会报空指针异常;
    //Optional<User> userNullOpt = Optional.of(null);//运行报NullPointerException,不用调用属性的时候才报错
    Optional<User> userOpt = Optional.of(new User("唐僧","男",18));

    // 3.构建一个value可以为null的optional对象
    Optional<User> userNullOpt = Optional.ofNullable(null);

    System.out.println("Optional 空对象");
    System.out.println(userEmptyOpt);

    System.out.println("Optional of对象必须存在,不然空指针异常");
    System.out.println(userOpt);

    System.out.println("Optional ofNullable,不存在返回empty()");
    System.out.println(userNullOpt);

}
复制代码
Optional 空对象
Optional.empty
Optional of 对象必须存在,不然空指针异常
Optional[User(username=唐僧, sex=男, age=18)]
Optional ofNullable,不存在返回empty()
Optional.empty
复制代码

3.获取类

 @Test
public void getOptionalTest(){
    Optional<User> userEmptyOpt = Optional.empty();

    Optional<User> userOpt = Optional.of(new User("唐僧","男",18));

    // 直接获取,注意如果value==null,会报NoSuchElementException异常
    User user01 = userOpt.get();

    // orElse可以传入一个 User 类型的对象作为默认值;
    // 当value!=null时,返回value值;
    // 当value==null时,返回默认值作为代替;
    User user02 = userEmptyOpt.orElse(new User("悟空","男",20));

    User temp = null; //Optional.ofNullable(temp) 返回 Optional.empty()
    User user03 =  Optional.ofNullable(temp).orElse(new User("八戒","男",22));

    // orElseGet和orElse不同的是orElseGet可以传入一段lambda表达式;
    // 当value!=null时,返回value值;
    // 当value==null时,使用该lambda返回的对象作为默认值;
    User user04 =  Optional.ofNullable(temp).orElseGet(()->getRandomUser(new Random().nextInt(3)));

    // orElseThrow可以传入一段lambda表达式,lambda返回一个Exception;
    // 当value!=null时,返回value值;
    // 当value==null时,抛出该异常;
    //User user05=  Optional.ofNullable(temp).orElseThrow(()->new RuntimeException("对象不存在"));
    //User user06=  Optional.ofNullable(temp).orElseThrow(NullPointerException::new);//方法引用

    System.out.println(user01);
    System.out.println(user02);
    System.out.println(user03);
    System.out.println(user04);

}
private User getRandomUser(int index){
    ArrayList<User> users = new ArrayList<>();
    users.add(new User("彭昱畅","男",24));
    users.add(new User("张子枫","女",20));
    users.add(new User("何炅","男",44));
    return users.get(index);
}
复制代码
User(username=唐僧, sex=男, age=18)
User(username=悟空, sex=男, age=20)
User(username=八戒, sex=男, age=22)
User(username=何炅, sex=男, age=44)
复制代码

3.转换类

@Test
public void mapOptionalValueTest(){
    Optional<User> userOpt = Optional.of(new User("hanpang","男",18));
    // 原来value的类型是User,经过map转换为Optional<String>
    Optional<String> userName = userOpt.map(User::getUsername);
    System.out.println(userName);
    System.out.println(userName.get());

    // 当map的入参也是一个Optional时,经过map转化后会形成Optional<Optional<String>>这种嵌套结构;
    // 但flatMap可以把这种嵌套结构打平;
    Optional<Optional<String>> unFlatMap = userOpt.map(user->Optional.of(user.getUsername()));
    System.out.println(unFlatMap);
    Optional<String> flatMap  = userOpt.flatMap(user->Optional.of(user.getUsername()));
    System.out.println(flatMap);
}
复制代码
Optional[hanpang]
hanpang
Optional[Optional[hanpang]]
Optional[hanpang]
复制代码

4.判断类

@Test
public void judgeOptionalValueTest(){
    Optional<User> userOpt = Optional.of(new User("刘德华","男",18));

    // filter传入一个lambda,lambda返回值为boolean;true:不做任何改变,false:返回一个空的optional;
    Optional<User> user01 = userOpt.filter(user->user.getAge()<20);
    System.out.println(user01);

    Optional<User> user02 = userOpt.filter(user->user.getAge()>20);
    System.out.println(user02);

    // isPresent就是判断value是不是null;我们在调用get之前,一定要先调用isPresent,因为直接如果value是null,直接调用get会报异常;
    User user = null;
    Optional<User> userOptional = Optional.ofNullable(user);
    if(userOptional.isPresent()){
        User u1 = userOptional.get();
        System.out.println("对象存在");
    }else{
        System.out.println("optional value==null");
    }

    // ifPresent传入一段lambda,当value!=null时,执行里面的逻辑;当当value==null时,啥都不干;
    user01.ifPresent(value -> System.out.println("optional value:" + value));
    user01.ifPresent(System.out::println);

}
复制代码
Optional[User(username=刘德华, sex=男, age=18)]
Optional.empty
optional value==null
optional value:User(username=刘德华, sex=男, age=18)
User(username=刘德华, sex=男, age=18)
复制代码

5.方法说明

方法 描述
empty 返回一个空的Optional对象
of 将指定值用Optional封装之后返回,如果该值是null,则抛出一个NullPointException
ofNullable 将指定值用Optional封装之后返回,如果该值是null,则返回一个空的Optional对象
get 如果值存在,将该值Optional封装返回,否则抛出一个NoSuchElementException异常
orElse 如果有值则将其返回,否则返回一个默认值
orElseGet 如果有值则将其返回,否则返回一个由指定的Supplier接口生成的值
orElseThrow 如果有值则将其返回,否则抛出一个由指定的Supplier接口生成的异常
map 如果值存在,就对该值执行提供的mapping方法的调用
flatMap 如果值存在,就对该值执行提供的mapping方法的调用,返回一个Optional类的值,否则就返回一个空Optional对象
filter 如果值存在并且满足提供的谓词(条件),就返回包含该值的 Optional 对象,否则返回一个空Optional对象
isPresent 如果存在就返回true,否则返回false
ifPresent 如果值存在,就执行使用该值的方法调用,否则什么都不做

(1)flatMap 方法与 map 方法作用一致

不过 flatMap 接收的参数 Function 要求返回一个 Optional 实例, 并且 flatMap 方法直接返回该结果, 而不对结果包装一层 Optional, 适用于 Optional 包含的值也是 Optional, 可以进行多层 Optional 的合并.

附录

(1)orElseThrow 的用途:在 SpringMVC 的控制器中,我们可以配置统一处理各种异常。

查询某个实体时,如果数据库中有对应的记录便返回该记录,否则就可以抛出

@RequestMapping("/{id}")
public User getUser(@PathVariable Integer id) {
    Optional<User> user = userService.getUserById(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);
}
复制代码

(2)多次使用 map 操作

@Test
public void moreMapTest(){
    User user = new User("han#pang","男",18);
    Optional<String> name = Optional
        .ofNullable(user)
        .map(u -> u.getUsername())
        .map(username -> username.toUpperCase())
        .map(username -> username.replace('#', ' '));
    System.out.println(name);
    System.out.println(name.get());
}
复制代码
Optional[HAN PANG]
HAN PANG
复制代码

(3)求字符串user的长度(为空的时候返回0)

// 从前台界面获取输入的用户信息
String user = getUserFromUI();
return Optional.ofNullable(user).orElse("").length;
复制代码

(4)循环遍历集合,我经常使用这种方式,很爽

传统方式:代码比较多

List<String> userList = getList();
if (list != null) {
  for(String user: list){
      System.out.println(user);
  }
}
复制代码

代码改进

List<String> userList = getList();
Optional.ofNullable(userList).orElse(new ArrayList<>()).forEach(user -> {
    System.out.println(user);
});
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享