一、Optional简介与使用
1.1 Optional简介
在Java应用开发的过程中,NPE(NullPointerException)问题是一个非常常见的问题,为了避免空指针异常对代码的破坏,我们不得不在各种可能出现空指针的地方通过if-else来校验,造成代码累赘,严重影响代码可读性。为了优雅的解决NPE问题,Google公司著名的Guava项目引入了Optional类,Guava通过检查空值的方式来防止代码污染,它鼓励程序员编写简单干净的代码。受到Google Guava项目的启发,Optional类已经成为Java 8类库的一部分
Optional类(java.util.Optional)是一个容器类,它可以保存类型T(任意类型)的值,成员变量value代表了这个值,或者仅保存null,表示这个值不存在,原来用null来表示一个值不存在,现在Optional可以更好的表达这个概念,并且可以避免空指针异常。通过使用Optional可以减少代码中的判空,实现函数式编程。
1.2 基本使用
//创建三个实体类,使用lombok自动生成相关getter和setter以及toString方法,相关注解省略
public class Member {
private int id;
private String name;
private int age;
private Address address;
}
public class Address {
private String detailAdd;
private Country country;
}
public class Country {
private String countryName;
private String countryCode;
}
获取用户所在地方的编号
//通过if-else来实现,需要进行层层判空检验
public String getMemberCountryCode(Member member){
String countryCode = "";
if (member != null){
Address address = member.getAddress();
if (address != null){
Country country = address.getCountry();
if (country != null){
countryCode = country.getCountryCode();
}
}
}
return countryCode;
}
//通过Optional类来实现
public String getMemberCountryCode(Member member){
String countryCode = "";
Optional<String> countryCodeOptional = Optional.ofNullable(member).map(Member::getAddress).map(Address::getCountry).map(Country::getCountryCode);
if (countryCodeOptional.isPresent()){
countryCode = countryCodeOptional.get();
}
return countryCode;
}
通过上面的例子,可以明显的看出,使用Optional类可以明显减少不必要的冗余代码,只需要在最后做一次判空检验即可,为空的时候还可以使用Optional的orElse、orElseGet来指定默认值,或者通过orElseThrow来抛出异常
//为空时,返回指定的默认值
countryCode = Optional.ofNullable(member).map(Member::getAddress).map(Address::getCountry).map(Country::getCountryCode).orElse("0710");
当Optional为空时,调用它的toString()方法也不会导致空指针,而是返回字符串”Optional.empty”
Member member = null;
Optional<Member> memberOptional = Optional.ofNullable(member);
System.out.println(memberOptional);
//输出结果
Optional.empty
1.3 常用方法
-
Optional对象的创建
在使用Optional时首先需要创建Optional对象,但Optional类的两个构造方法都是private型的,可通过Optional类提供的三个静态方法来创建Optional对象
//创建包装对象值为null的Optional对象,一般不使用 Optional<Object> emptyOptional = Optional.empty(); //创建包装对象不为空的Optional对象 Optional<String> optional = Optional.of("lizhi"); //创建包装对象可以为null的Optional对象 Optional<String> nullAbleOptional = Optional.ofNullable(null);
-
数据处理
Optional提供了map、filter以及flatMap方法来对包装对象进行操作
//通过map映射获取member对象的Address的包装Optional对象 Optional<Address> addressOptional = Optional.ofNullable(member).map(Member::getAddress)
如果member为空,则会返回一个空的Optional对象,不会导致空指针
//通过filter判断member对象年龄是否大于24 Optional<Member> memberOptional = Optional.ofNullable(member).filter(m -> m.getAge() > 24);
filter方法中有值且满足判断条件则返回包含该值的Optional,否则返回空的Optional
//使用flatMap方法获取映射的address的包装Optional对象 Optional<Address> addressOptional = Optional.ofNullable(member).flatMap(m -> Optional.ofNullable(m.getAddress()));
flatMap方法与map方法基本一样,不同的是map方法的mapping函数返回的可以是任何类型T,flatMap方法的mapping函数返回的必须是Optional类型
-
获取值
Optional类主要提供了四种获取值的方法,但这些方法又各不相同
//get()方法,如果Optional为空,则get方法将抛出异常 Member memberInfo = Optional.ofNullable(member).get(); //orElse()方法,如果Optional不为空,则将其返回,如果为空则返回指定的值 Member memberInfo = Optional.ofNullable(member).orElse(new Member(1, "lizhi", 24, null)); //orElseGet()方法,与orElse()方法类似,区别在于指定默认值的方式不同,orElse直接将传入的值作为默认值,而orElseGet()则通过 Member memberInfo = Optional.ofNullable(member).orElseGet(() -> new Member(1, "lizhi", 24, null)); //orElseThrow()方法,如果Optional不为空则将其返回,否则直接抛出异常 Member memberInfo = Optional.ofNullable(member).orElseThrow(IllegalArgumentException::new);
二、Optional源码分析
2.1 构造方法
//空的Optional实例
private static final Optional<?> EMPTY = new Optional<>();
//Optional包装的原始数据,可以是任何类型,用变量value表示
private final T value;
//无参构造
private Optional() {
this.value = null;
}
//有参构造
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
Optional类提供了两个私有的构造方法,仅供类内部调用
2.2 实例方法
//直接返回Optional类的空实例EMPTY
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
//参数不能为null,有参构造器对参数进行了判空检验,如果参数为null,将会导致空指针
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
//参数可以为空,如果参数为空相当于调用了empty方法,参数不为空时相当于调用了of方法
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
2.3 判断是否为空
public boolean isPresent() {
return value != null;
}
//如果源数据不为null,则调用参数中的accept方法对数据进行处理;如果为空则什么也不做
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
2.4 数据处理
//如果过滤条件为null,则直接抛出空指针异常,如果Optional为空则直接返回空实例,否则判断是否满足过滤条件,如果满足则返回实例,不满足就返回一个空的Optional实例
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
//与filter的结构一致,先判断参数是否为空,然后判断当前Optional实例时是否为空,不为空则执行apply映射函数,然后将映射后的数据封装成Optional类的实例返回
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));
}
}
//与map逻辑基本一致,map方法的apply函数返回的是任意类型,而flatMap方法的apply函数返回的只能是Optional类型或者null,所以flatMap对返回值进行了判空检验,如果映射函数返回的是null则直接抛出空指针异常
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));
}
}
2.5 数据获取
//get()方法,如果Optional的实例为空时,则抛出以下异常信息
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
//指定与原始类型相同的默认值,如果Optional不为空则返回该实例包装的数据,否则返回默认值
public T orElse(T other) {
return value != null ? value : other;
}
//与orElse()方法一致,不同的是orElseGet()方法可以通过函数式接口来指定默认值
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
//如果Optional实例不为空,则返回原始包装数据,如果为空就抛出定义的异常
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
2.6 Object方法
//即使Optional为一个空实例,调用toString()方法也不会出现空指针
public String toString() {
return value != null
? String.format("Optional[%s]", value)
: "Optional.empty";
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/153573.html