在开发过程中,经常会遇到 NullPointerException 运行时异常。为了能更好的处理空指针问题, 从 java 1.8版本后提供了 java.util.Optional 工具类。
Optional API 介绍
属性值
private final T value;
private static final Optional<?> EMPTY = new Optional<>();
- value:Optional对象持有的需要进行业务对象。
- EMPTY: 为value 为空的 Optional 对象。
Optional 对象创建
public static<T> Optional<T> empty() {
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
- empty() 创建一个value为null的 Optional对象。
- of(T value) value不允许为null。
- ofNullable(T value) 最常用的Optional对象创建方式,value允许为null。
业务对象判空和对象获取
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
public boolean isPresent() {
return value != null;
}
- 通过get方法 我们可以获取到value对象,注意如果value为null 会抛出 NoSuchElementException 异常
- isPresent 方法用来判断value 是否为空
有些小伙伴可能有疑问了,这个工具类并没有比之前写的非空判断更优雅。比如
if(object != null){
//doSomething
}
在java 1.8版本后,提供了函数式接口。下面我们来看下Optional 结合函数式接口的例子,通过例子来进一步感受下带来的变化。
java.util.Optional#ifPresent
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
例子
Column col = getColumn();
Optional.ofNullable(col).ifPresent(System.out::println);
java.util.Optional#filter
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
例子
Column column = getColumn();
Optional.ofNullable(column).filter(col -> "length".equals(col.getName())).ifPresent( t -> System.out.println("exist"));
java.util.Optional#map java.util.Optional#flatMap
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));
}
}
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));
}
}
例子
public class Table{
public Optional<Column> getColumn(Table t){
//doSomthing
}
}
Table table = new Table();
Optional.ofNullable(table).map(Table :: getColumn);
Optional.ofNullable(table).flat map (Table :: getColumn);
通过map和flatMap我们得到的结果如下图
- map方法将返回结果又套了一层Optional。
- flatMap直接将结果返回。
java.util.Optional#orElse java.util.Optional#orElseGet
public T orElse(T other) {
return value != null ? value : other;
}
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
例子
Column column = getColum();
Optional.ofNullable(column).orElse(new Column("default"));
Optional.ofNullable(column).orElseGet(() -> new Column("default"));
java.util.Optional#orElseThrow
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
例子
Column column = getColum();
Optional.ofNullable(column).orElseThrow(RuntimeException::new);
使用样例
我们有如下数据结构的 Database 对象,如果要获取到name属性我们可以按照下面那样操作
database.getTable().getColumn().getName();
看起来上面的代码可读性很好,但是不是每个database 都能获取到对应的table,所以上面的代码存在NPE的风险
我们可以按照下面的方式修改
if(database != null){
Table table = database.getTable();
if(table != null){
Column column = table.getColumn();
if(column != null){
String name = column.getName()
}
}
}
为了避免空指针我们使用了很多的if逻辑判断,造成代码可读性大大降低。
Optional.ofNullable(database).flatMap(DataBase::getTable)
.flatMap(Table::getColumn)
.map(Column::getName)
.orElse("UNKNOW");
通过Optional工具的使用 增加了代码的可读性。掌握Optional类后,对于空指针预防我们又多了一种处理选择。