Hi,大家好,我是抢老婆酸奶的小肥仔。
在我们日常开发中NullPointerException是永远绕不过去的坑,代码写的不好,天天都会被它折磨的死去活来,今天我们来介绍下Optional,这个能让我们缓解NullPointerException恐惧的工具。
在没有Optional之前,我们为了避免NullPointerException很多时候都是使用if-else-if来进行条件判断,然后根据条件来获取对应数据。例如:获取赵一所在的职位名称。
java
List<Expert> experts = Arrays.asList(
new Expert("jiashn","男",20,new Position("总经理",1)),
new Expert("张三","女",21,new Position("财务总监",2)),
new Expert("王五","女",26,new Position("会计",3)),
new Expert("李四","男",24,new Position("开发部经理",2))
);
//获取赵一所在的职位名称
if(CollectionUtils.isNotEmpty(experts)){
for (Expert expert : experts) {
if (Objects.equals("赵一",expert.getName())){
Position position = expert.getPosition();
if (Objects.nonNull(position)){
System.out.println(position.getName());
}
}
}
}
使用optional就会优雅很多,也会尽量的避免NPE。如:
java
String name = Optional.of(users.stream().filter(user -> Objects.equals("赵一", user.getName())).findFirst())
.get()
.map(SystemUser::getPosition)
.map(Position::getName)
.orElse("");
System.out.println("Optional获取职位名称:" + name);
上述代码对比中,我们可以清晰感觉到使用Optional代码比较优雅,也没有大量代码判空,即使数据不存在也没报NPE,避免了NPE.
Optional是JDK1.8后引入的一个新特性,通常被认为是用于缓解java中NPE问题。Optional作为容器,可以保存类型T的值,也可以直接保存null。
接下来我们介绍下Optional的一些方法。
Optional方法
准备工作,创建一个有数据的系统用户,一个为null的数据。
java
/**
* @author: jiangjs
* @description: 系统用户
* @date: 2023/5/8 11:40
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SystemUser {
private String name;
private String gender;
private Integer age;
private Position position;
public Optional<Position> optionalPosition(){
return Optional.ofNullable(position);
}
}
/**
* @author: jiangjs
* @description: 职位
* @date: 2023/5/8 11:20
**/
@Data
@AllArgsConstructor
public class Position {
/**
* 职位名称
*/
private String name;
/**
* 职位等级
*/
private Integer level;
}
java
SystemUser systemUser = new SystemUser("王五","女",26,new Position("会计",3));
SystemUser user = null;
empty
static<T> Optional<T> empty()
:静态方法,返回空的Optional。
源码:
java
private static final Optional<?> EMPTY = new Optional<>();
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
实现:
java
//empty
Object o = Optional.empty();
System.out.println("empty:" + o);
输出:
💡 empty:Optional.empty
of、ofNullable
static <T> Optional<T> of(T value)
:创建一个非空的Optional,当T为null时,则抛出NPE。
static <T> Optional<T> ofNullable(T value)
:如果T不为空,则创建指定值的Optional,否则返回空的Optional。
java
//of ofNullable
Optional<SystemUser> systemUserOptional = Optional.of(systemUser);
System.out.println("systemUserOptional:" + systemUserOptional);
Optional<SystemUser> userOptional = Optional.of(user);
System.out.println("userOptional:" + userOptional);
Optional<SystemUser> optionalSystemUser = Optional.ofNullable(systemUser);
System.out.println("optionalSystemUser:" + optionalSystemUser);
Optional<SystemUser> optionalUser = Optional.ofNullable(user);
System.out.println("optionalUser:" + optionalUser);
输出:
💡 systemUserOptional:Optional[SystemUser(name=王五, gender=女, age=26, position=Position(name=会计, level=3))]
💡 java.lang.NullPointerException
💡 optionalSystemUser:Optional[SystemUser(name=王五, gender=女, age=26, position=Position(name=会计, level=3))]💡 optionalUser:Optional.empty
Optional.of(user):报了空指针异常,这是因为of在创建Optional时,会对T进行判空处理。源码:
java
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
Optional.ofNullable(user):使用了三目运算符,如果T为空,则调用empty(),返回空的Optional。 源码:
java
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
isPresent()
isPresent()
:判断是否包含指定值,包含返回true,反之返回false。
java
//isPresent
boolean present = Optional.of(systemUser).isPresent();
System.out.println("systemUser是否存在:" + present);
boolean judge = Optional.ofNullable(user).isPresent();
System.out.println("user是否存在:" + judge);
输出:
💡systemUser是否存在:true
💡 user是否存在:false
isPresent():直接获取Optional中的T来判空。源码:
java
public boolean isPresent() {
return value != null;
}
注:在写代码时,不要即用了if,又用isPresent来进行判断,如:if (Optional.ofNullable(user).isPresent()){业务代码},这样消除不了繁琐的if判断,也创建了Optional对象。
orElse、orElseGet
T orElseGet(Supplier<? extends T> other)
:如果调用对象包含值,则返回该值,否则返回other。
T orElse(T other)
:如果调用对象包含值,则返回该值,否则返回other。
java
//orElseGet,orElse
SystemUser orElseGetUser = Optional.ofNullable(user).orElseGet(UseOptional::getSystemUser);
System.out.println("orElseGetUser:" + orElseGetUser);
SystemUser orElseUser = Optional.ofNullable(user).orElse(getSystemUser());
System.out.println("orElseUser:" + orElseUser);
System.out.println("-------------------------------------------------------------");
SystemUser orElseGetSystemUser = Optional.of(systemUser).orElseGet(UseOptional::getSystemUser);
System.out.println("orElseGetSystemUser:" + orElseGetSystemUser);
SystemUser orElseSystemUser = Optional.of(systemUser).orElse(getSystemUser());
System.out.println("orElseSystemUser:" + orElseSystemUser);
输出:
默认数据:SystemUser(name=李四, gender=男, age=24, position=Position(name=开发部经理, level=2))
💡 orElseGetUser:SystemUser(name=李四, gender=男, age=24, position=Position(name=开发部经理, level=2))
默认数据:SystemUser(name=李四, gender=男, age=24, position=Position(name=开发部经理, level=2))
💡 orElseUser:SystemUser(name=李四, gender=男, age=24, position=Position(name=开发部经理, level=2))
💡 orElseGetSystemUser:SystemUser(name=王五, gender=女, age=26, position=Position(name=会计, level=3))
默认数据:SystemUser(name=李四, gender=男, age=24, position=Position(name=开发部经理, level=2))
💡 orElseSystemUser:SystemUser(name=王五, gender=女, age=26, position=Position(name=会计, level=3))
当调用对象不包含值时,虽然两者都是返回other,但是从定义中我们可以看到orElseGet是使用了Supplier函数式编程,而orElse直接使用T。上面例子也说明了orElse会提前创建T而orElseGet会在判断调用对象没有时才会调用Supplier来创建other。
orElse与orElseGet区别:
💡 orElse:不管调用对象是否存在,都会提前创建T。
💡 orElseGet:只有当调用对象不存在时,才会使用Supplier创建返回值。
get
get():即获取Optional的对象数据,如果对象数据不存在则抛出异常:NoSuchElementException
java
//get
SystemUser getSystemUser = Optional.ofNullable(systemUser).get();
System.out.println("getSystemUser:" + getSystemUser);
SystemUser getUser = Optional.ofNullable(user).get();
System.out.println("getUser:" + getUser);
输出:
💡 getSystemUser:SystemUser(name=王五, gender=女, age=26, position=Position(name=会计, >level=3))
💡 Exception in thread "main" java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:135)
at com.jiashn.springbootproject.useUtil.UseOptional.main(UseOptional.java:86)
map、flatMap
<U> Optional<U> map(Function<? super T, ? extends U> mapper)
:如果有值,则进行数据处理,如果没有值,则返回Optional.empty()。
<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
:与map类似,要求返回值必须是Optional。
java
//map
String sysUserName = Optional.of(systemUser).map(SystemUser::getName).get();
System.out.println("sysUserName:" + sysUserName);
Optional<String> userName = Optional.ofNullable(user).map(SystemUser::getName);
System.out.println("userName:" + userName);
//flatMap
String name = Optional.of(systemUser)
.flatMap(SystemUser::optionalPosition)
.map(Position::getName).get();
System.out.println("name:" + name);
输出:
💡 sysUserName:王五
💡 userName:Optional.empty
💡 name:会计
上述就是Optional提供一些方法,我们在实际开发中应灵活运用,通常Optional会跟Stream进行联合使用。例如:获取上述集合性别为女的,然后获取过滤后集合的第一个元素,数据不存在时创建新的对象。
java
Expert expert = Optional.of(experts.stream().filter(ex -> Objects.equals("女",ex.getGender())).findFirst())
.get()
.orElseGet(Expert::new);
这样子在后面使用expert就能避免出现NPE.
jdk引入了Optional能让我们快捷的获取比较复杂的数据结构中的数据,同时也能有效避免NPE,当然Optional方法单独使用可能效果不大,但是组合使用时会让我们事半功倍。