Optional 的来历
Optional是一个包含有可选值的包装类,简单点,就是为了防 NullPointerException(NPE),比如
String result = test.getName().getTime().getNum().getAnswer();
源码是这样形容的:
A container object which may or may not contain a non-null value.
Additional methods that depend on the presence or absence of a contained value are provided
"它是一个容器,可能包含一个非null元素(也可能不包含元素),提供了一系列的方法供我们判断该容器里的对象是否存在以及后续的操作"
注意到Optional也是一个final的类,另外Optional 没有implement java.io.Serializable,因此,它不应该用作类的属性
下面是和Stream的对比图,摘自网上:
如何创建Optional
//空的Optional容器,内部没有任何对象 private static final Optional<?> EMPTY = new Optional<>(); //容器中的对象 private final T value; //私有构造方法 private Optional() { this.value = null; } //返回一个不含对象的空Optional容器 public static<T> Optional<T> empty() { @SuppressWarnings("unchecked") Optional<T> t = (Optional<T>) EMPTY; return t; } //私有的带参构造方法,参数就是具体要装载的对象,传null,抛出NPE private Optional(T value) { this.value = Objects.requireNonNull(value); }// 创建装载了value的Optional容器,若value==null,抛出NPE 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); }
这里我们给出一个最佳实践:创建Optional,请用ofNullable(T value)
Optional 的常用方法和函数
这里也给出一个最佳实践:尽量不要使用isPresent()和get,否则又回到原来的判断空值老套路
掌握取值的一个重要方法:orElse(T):T
// 如果容器中对象value存在,则返回value,否则返回传递进来的参数(作为你期望的默认返回值) public T orElse(T other) { return value != null ? value : other; }
====================截止目前,还没体现Optional的优越感===========================
一个对比例子
大家经常会碰到,这是通常的if判空的写法:
public String getName(User user){ if(user == null){ return "unknown"; }
return user.getName(); }
使用Optional改造失败,反而变复杂的例子:
public String getName(User user){ Optional<User> u = Optional.ofNullable(user); if(!u.isPresent()){ return "unknown"; } return u.get().getName(); }
这样改写无非就是用isPresent方法来替代原先user==null,改写失败,姿势不对,改写成功的例子:
public String getName(User user){ return Optional.ofNullable(user).map(u -> u.getName()).orElse("unknown"); }
按照这个思路,可以顺利的进行链式调用,甩掉if esle null 的层层判断了
Optional 高阶姿势
1、ifPresent
//接收一个consumer函数接口,装载对象存在则调用接口的accept方法
public void ifPresent(Consumer<? super T> consumer) { if (value != null) consumer.accept(value); } @FunctionalInterface public interface Consumer<T> { void accept(T t); }
改造上述例子:
旧写法: if (user != null) { System.out.println(user.getName()); }
新写法:
Optional.ofNullable(user).ifPresent((value)->System.out.println(value.getName()))
2、orElseGet和orElseThrow
// 如果对象存在,则直接返回,否则返回由Supplier接口的实现用来生成默认值 public T orElseGet(Supplier<? extends T> other) { return value != null ? value : other.get(); } @FunctionalInterface public interface Supplier<T> { T get(); } // 如果对象存在,则直接返回,否则抛出supplier接口创建的异常 public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { if (value != null) { return value; } else { throw exceptionSupplier.get(); }
}
改造上述例子:
// 旧写法 if (user != null) { user = new User(); } //新写法 Optional.ofNullable(user).orElseGet(()-> new User());
注意:orElseGet()和orElse()差不多,只不过它可以通过Supplier接口的实现来生成默认值,后者是直接提供一个默认值
3、filter
// 如果对象存在,并且符合过滤条件,返回对象的Optional容器,否则返回一个空Optional容器 public Optional<T> filter(Predicate<? super T> predicate) { Objects.requireNonNull(predicate); if (!isPresent()) return this; else return predicate.test(value) ? this : empty(); } @FunctionalInterface public interface Predicate<T> { boolean test(T t); }
注意,返回的是Optional对象,于是我们可以继续链式调用了
实例:
Optional.ofNullable(user).filter( u -> u.getAge() > 18).ifPresent(u -> System.out.println("age > 18."));
4、map和flatMap
// 如果对象存在,则返回 调用mapping函数得到的返回值创建的Optional,否则返回空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区别在于apply函数的返回值不同。前者是? extends U,后者必须是Optional 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)); } } @FunctionalInterface
public interface Function<T, R> { R apply(T t); }
示例:
Optional.ofNullable(user).map(u-> u.getName()).orElse("unknown")
Optional总结
这是个稍微野鸡一点的总结:
1、filter、map、flatMap三个函数,源码第一句都是
Objects.requireNonNull(mapper);
因此可以放心安全的编写"不为空时候的逻辑",然后配合 orElse系列或ifPresent处理取值和其他逻辑
2、能取值的是orElseGet(),orElse()
3、不要使用isPresent(),get(),这是倒退(轻一点叫不推荐)
4、使用Optional只是"更好看"(优雅?),通过方法隐藏了NPE,不会较少业务逻辑,不会简化"很多"代码