这是我参与更文挑战的第10天
基于Java8的介绍,在Java9中对
Optional
的增强
1.Optional介绍
Optional是Jdk1.8提供的一个新类,希望可以通过该类的引入解决令人烦躁的null判断问题,非常好用。
该类有点类似于包装类,将要操作的Java类封装到该类的对象里面,同时将一些常用的判断逻辑封装为成员方法,结合lambda语法,实现比较优雅的链式调用。
-
构建API:构建一个Optional对象;方法有:
empty()
、of()
、ofNullable()
-
获取API:获取Optional对象里包装的值;方法有:
get()
、orElse()
、orElseGet()
、orElseThrow()
-
转换API:将Optional对象里包装的值转换成一个新的值;方法有:
map()
、flatMap()
-
判断API:对Optional对象里包装的值做一些逻辑判断;方法有:
filter()
、isPresent()
、ifPresent()
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);
});
复制代码