Optional入门
Optional是jdk1.8引入的类型,Optional是一个容器对象,它包括了我们需要的对象,使用isPresent方法判断所包含对象是否为空,isPresent方法返回false则表示Optional包含对象为空,否则可以使用get()取出对象进行操作。
Optional的优点是:
1、提醒你非空判断。
2、将对象非空检测标准化。
//of():为非null的值创建一个Optional Optional<String> optional = Optional.of("bam"); // isPresent(): 如果值存在返回true,否则返回false optional.isPresent(); // true //get():如果Optional有值则将其返回,否则抛出NoSuchElementException optional.get(); // "bam" //orElse():如果有值则将其返回,否则返回指定的其它值 optional.orElse("fallback"); // "bam" //ifPresent():如果Optional实例有值则为其调用consumer,否则不做处理 optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b"
使用案例
//修改 @Test public void testUpdate() { Optional<CmsPage> optional = cmsPageRepository.findOne("5b17a34211fe5e2ee8c116c9"); if(optional.isPresent()){ CmsPage cmsPage = optional.get(); cmsPage.setPageName("测试页面01"); cmsPageRepository.save(cmsPage); } }
Optional API
Optional提供很多有用的方法,这样我们就不用显式进行空值检测。Optional 类的引入很好的解决空指针异常。
Optional API地址:https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html
static <T> Optional<T> ofNullable(T value):当value值非空时,则返回一个非空的Optional,否则返回一个空的Optional
<U> Optional<U> map(Function<? super T,? extends U> mapper):如果value值存在,则执行提供的映射函数,如果结果不为空,返回一个非空的Optional
T orElse(T other):如果value值存在则返回value值,如果不存在,则返回other。
<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier):如果value值存在,则返回,如果不存在,则抛出一个由Supplier创建的异常。
void ifPresent(Consumer<? super T> consumer):如果value值存在,则调用Consumer接口(执行Lambda表达式或方法引用),否则什么都不做。
Optional<T> filter(Predicate<? super T> predicate):如果值存在且值匹配predicate,返回一个非空的Optional,否则返回一个空的Optional。
T orElseGet(Supplier<? extends T> other):如果值存在,则返回该值,否则调用other并返回调用的结果
Optional API的应用
善用 Optional 可以使我们代码中很多繁琐、丑陋的设计变得十分优雅。
使用 Optional,我们就可以把下面这样的代码进行改写。
public static String getName(User u) { if (u == null || u.name == null) return "Unknown"; return u.name; }
不过,千万不要改写成这副样子。
public static String getName(User u) { Optional<User> user = Optional.ofNullable(u); if (!user.isPresent()) return "Unknown"; return user.get().name; }
这样改写非但不简洁,而且其操作还是和第一段代码一样。无非就是用 isPresent
方法来替代 u==null
。这样的改写并不是 Optional 正确的用法,我们再来改写一次。
public static String getName(User u) { return Optional.ofNullable(u) .map(user->user.name) .orElse("Unknown"); }
这样才是正确使用 Optional 的姿势。那么按照这种思路,我们可以安心的进行链式调用,而不是一层层判断了。看一段代码:
public static String getChampionName(Competition comp) throws IllegalArgumentException { if (comp != null) { CompResult result = comp.getResult(); if (result != null) { User champion = result.getChampion(); if (champion != null) { return champion.getName(); } } } throw new IllegalArgumentException("The value of param comp isn't available."); }
让我们看看经过 Optional 加持过后,这些代码会变成什么样子。
public static String getChampionName(Competition comp) throws IllegalArgumentException { return Optional.ofNullable(comp) .map(Competition::getResult) // 相当于c -> c.getResult(),下同 .map(CompResult::getChampion) .map(User::getName) .orElseThrow(()->new IllegalArgumentException("The value of param comp isn't available.")); }
还有很多不错的使用姿势,比如字符串为空则不打印可以这么写:
string.ifPresent(System.out::println);
Optional 的魅力还不止于此,Optional 还有一些神奇的用法,比如 Optional 可以用来检验参数的合法性。
public void setName(String name) throws IllegalArgumentException { this.name = Optional.ofNullable(name) .filter(User::isNameValid) .orElseThrow(()->new IllegalArgumentException("Invalid username.")); }
这样写参数合法性检测,应该足够优雅了吧。
不过这还没完,上面的两个例子其实还不能完全反应出 Optional 的设计意图。事实上,我们应该更进一步,减少 Optional.ofNullable
的使用。为什么呢?因为 Optional 是被设计成用来代替 null 以表示不确定性的,换句话说,只要一段代码可能产生 null,那它就可以返回 Optional。而我们选择用 Optional 代替 null 的原因,是 Optional 提供了一个把若干依赖前一步结果的处理结合在一起的途径。
Optional应用建议
Optional 就像一个处理不确定性的管道,我们在一头丢进一个可能是 null 的东西(接口返回结果),经过层层处理,最后消除不确定性。Optional 在过程中保留了不确定性,从而把对 null 的处理移到了若干次操作的最后,以减少出现 NPE 错误的可能。于是,Optional 应用的建议也呼之欲出了:
-
适用于层级处理(依赖上一步操作)的场合。
-
产生对象的方法若可能返回 null,可以用 Optional 包装。
-
尽可能延后处理 null 的时机,在过程中使用 Optional 保留不确定性。
-
尽量避免使用 Optional 作为字段类型。