以下文章来源于 Hollis,作者上帝爱吃苹果
对于 Java 程序员来说,null 是令人头痛的货色。时常会受到空指针异样(NPE)的骚扰。连 Java 的发明者都抵赖这是他的一项微小失误。
那么,有什么方法能够防止在代码中写大量的判空语句呢?
有人说能够应用 JDK8 提供的 Optional 来防止判空,然而用起来还是有些麻烦。
作者在日常工作中,封装了一个工具,能够能够链式调用对象成员而无需判空,相比原有的 if null 逻辑 和 JDK8 提供的 Optional 更加优雅易用,在工程实际中大大提高了编码效率,也让代码更加的精准和优雅。
不优雅的判空调用
我想从事 Java 开发的小伙伴必定有遇到过上面这种让人好受的判空逻辑:当初有一个 User 类,School 是它的成员变量
/**
* @author Axin
* @since 2020-09-20
* @summary 一个 User 类定义
*(Ps:Data 是 lombok 组件提供的注解,简化了 get set 等等的约定代码)*/
@Data
public class User {
private String name;
private String gender;
private School school;
@Data
public static class School {
private String scName;
private String adress;
}
}
当初想要取得 School 的成员变量 adress , 个别的解决形式:
public static void main(String[] args) {User axin = new User();
User.School school = new User.School();
axin.setName("hello");
if (Objects.nonNull(axin) && Objects.nonNull(axin.getSchool())) {User.School userSc = axin.getSchool();
System.out.println(userSc.getAdress());
}
}
获取 adress 时要对 School 进行判空,尽管有些麻烦,到也能用,通过 JDK8 提供的 Optional 工具也是能够,但还是有些麻烦。
而下文的 OptionalBean 提供一种能够链式一直地调用成员变量而无需判空的办法,间接链式调用到你想要获取的指标变量,而无需放心空指针的问题。
链式调用成员变量
如果用了本文设计的工具 OptionalBean,那么上述的调用能够简化成这样:
public static void main(String[] args) {User axin = new User();
User.School school = new User.School();
axin.setName("hello");
// 1. 根本调用
String value1 = OptionalBean.ofNullable(axin)
.getBean(User::getSchool)
.getBean(User.School::getAdress).get();
System.out.println(value1);
}
执行后果:
其中 User 的 school 变量为空,能够看到代码并没有空指针,而是返回了 null。这个工具怎么实现的呢?
OptionalBean 工具
/**
* @author Axin
* @since 2020-09-10
* @summary 链式调用 bean 中 value 的办法
*/public final class OptionalBean<T> {private static final OptionalBean<?> EMPTY = new OptionalBean<>();
private final T value;
private OptionalBean() {this.value = null;}
/**
* 空值会抛出空指针
* @param value
*/
private OptionalBean(T value) {this.value = Objects.requireNonNull(value);
}
/**
* 包装一个不能为空的 bean
* @param value
* @param <T>
* @return
*/
public static <T> OptionalBean<T> of(T value) {return new OptionalBean<>;(value);
}
/**
* 包装一个可能为空的 bean
* @param value
* @param <T> * @return
*/
public static <T> OptionalBean<T> ofNullable(T value) {return value == null ? empty() : of(value);
}
/**
* 取出具体的值
* @param fn
* @param <R> * @return
*/
public T get() {return Objects.isNull(value) ? null : value;
}
/**
* 取出一个可能为空的对象
* @param fn
* @param <R> * @return
*/
public <R> OptionalBean<R> getBean(Function<? super T, ? extends R> fn) {return Objects.isNull(value) ? OptionalBean.empty() : OptionalBean.ofNullable(fn.apply(value));
}
/**
* 如果目标值为空 获取一个默认值
* @param other
* @return
*/
public T orElse(T other) {return value != null ? value : other;}
/**
* 如果目标值为空 通过 lambda 表达式获取一个值
* @param other
* @return
*/
public T orElseGet(Supplier<? extends T> other) {return value != null ? value : other.get();
}
/**
* 如果目标值为空 抛出一个异样
* @param exceptionSupplier
* @param <X> * @return
* @throws X
*/
public <X extends Throwable>; T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {if (value != null) {return value;} else {throw exceptionSupplier.get();
}
}
public boolean isPresent() {return value != null;}
public void ifPresent(Consumer<? super T> consumer) {if (value != null)
consumer.accept(value);
}
@Override
public int hashCode() {return Objects.hashCode(value);
}
/**
* 空值常量
* @param <T> * @return
*/
public static <T> OptionalBean<T> empty() {@SuppressWarnings("unchecked")
OptionalBean<T> none = (OptionalBean<T>) EMPTY;
return none;
}
}
工具设计次要参考了 Optional 的实现,再加上对链式调用的扩大就是上述的 OptionalBean。
使用手册
能够看到代码中也提供了和 Optional 一样的扩大办法,如 ifPresent()、orElse()等等:
public static void main(String[] args) {User axin = new User();
User.School school = new User.School();
axin.setName("hello");
// 1. 根本调用
String value1 = OptionalBean.ofNullable(axin)
.getBean(User::getSchool)
.getBean(User.School::getAdress).get();
System.out.println(value1);
// 2. 扩大的 isPresent 办法 用法与 Optional 一样
boolean present = OptionalBean.ofNullable(axin)
.getBean(User::getSchool)
.getBean(User.School::getAdress).isPresent();
System.out.println(present);
// 3. 扩大的 ifPresent 办法
OptionalBean.ofNullable(axin)
.getBean(User::getSchool)
.getBean(User.School::getAdress)
.ifPresent(adress -> System.out.println(String.format("地址存在:%s", adress)));
// 4. 扩大的 orElse
String value2 = OptionalBean.ofNullable(axin)
.getBean(User::getSchool)
.getBean(User.School::getAdress).orElse("家里蹲");
System.out.println(value2);
// 5. 扩大的 orElseThrow
try {String value3 = OptionalBean.ofNullable(axin)
.getBean(User::getSchool)
.getBean(User.School::getAdress).orElseThrow(() -> new RuntimeException("空指针了"));
} catch (Exception e) {System.out.println(e.getMessage());
}
}
run 一下:
总结
设计了一种能够链式调用对象成员而无需判空的工具让代码更加的精准和优雅,如果本文设计的工具满足了刚好解决你的困扰,那就在我的项目中应用吧!