一、Optional入门
Optional是jdk1.8引入的类型,Optional是一个容器对象,它包括了我们需要的对象,使用isPresent方法判断所包含对象是否为空,isPresent方法返回false则表示Optional包含对象为空,否则可以使用get()取出对象进行操作。
之前的写法:
public Person getPerson() { Person person = new Person(); if (null == person) { return null; } return person; }
现在可以写成:
public Person getPerson() { Person person = new Person(); return Optional.ofNullable(person).orElse(null); }
其中Person类
@Data public class Person { private String name; private Integer age; }
Optional的优点是:
1、提醒你非空判断。
2、将对象非空检测标准化。
首先我们先打开Optional的内部,去一探究竟 先把几个创建Optional对象的方法提取出来
public final class Optional<T> { private static final Optional<?> EMPTY = new Optional<>(); private final T value; //我们可以看到两个构造方格都是private 私有的 //说明 我们没办法在外面去new出来Optional对象 private Optional() { this.value = null; } private Optional(T value) { this.value = Objects.requireNonNull(value); } //这个静态方法大致 是创建出一个包装值为空的一个对象因为没有任何参数赋值 public static<T> Optional<T> empty() { @SuppressWarnings(unchecked) Optional<T> t = (Optional<T>) EMPTY; return t; } //这个静态方法大致 是创建出一个包装值非空的一个对象 因为做了赋值 public static <T> Optional<T> of(T value) { return new Optional<>(value); } //这个静态方法大致是 如果参数value为空,则创建空对象,如果不为空,则创建有参对象 public static <T> Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); } }
1、我们可以看到两个构造方格都是private 私有的,说明我们没办法在外面去new出来Optional对象。
2、静态方法:empty方法创建出一个包装值为空的对象。of方法创建出一个包装值非空的对象,ofNullable方法创建包装对象值可以为空也可以不为空的对象。
// 1、创建一个包装对象值为空的Optional对象 Optional<String> optEmpty = Optional.empty(); // 2、创建包装对象值非空的Optional对象 Optional<String> optOf = Optional.of("optional"); // 3、创建包装对象值允许为空也可以不为空的Optional对象 Optional<String> optOfNullable1 = Optional.ofNullable(null); Optional<String> optOfNullable2 = Optional.ofNullable("optional");
常用方法:
//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"
使用案例1
//修改 @Test public void testUpdate() { Optional<CmsPage> optional = cmsPageRepository.findOne("5b17a34211fe5e2ee8c116c9"); if(optional.isPresent()){ CmsPage cmsPage = optional.get(); cmsPage.setPageName("测试页面01"); cmsPageRepository.save(cmsPage); } }
使用案例2:判断从JSONObject中取出的字符串是否为空
Object access_token = tokenResult.get("access_token"); String o = (String) Optional.ofNullable(access_token).orElse("");
简写:
String accessToken = (String) Optional.ofNullable(tokenResult.get("access_token")).orElse("");
实际开发中,这种判断比较常见。我们经常会对取出的值进行判断,如果为null,则赋值为空字符串,否则为本身。
二、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并返回调用的结果
1、Optional.get()方法(返回对象的值)
get()方法是返回一个option的实例值 源码:
public T get() { if (value == null) { throw new NoSuchElementException("No value present"); } return value; }
例子:
// 1、创建一个包装对象值为空的Optional对象 Optional<String> optEmpty = Optional.empty(); String s = optEmpty.get();
结果:java.util.NoSuchElementException: No value present
2、Optional.isPresent()方法(判读是否为空)
isPresent()方法就是会返回一个boolean类型值,如果对象不为空则为真,如果为空则false 源码:
public boolean isPresent() { return value != null; }
故在get之前需要调用isPresent方法进行判断
Optional<String> optOf = Optional.of("optional"); if (optOf.isPresent()) { System.out.println(optOf.get()); }
例2:
Person person = new Person(); person.setAge(2); Optional<Person> optional = Optional.ofNullable(person); if (optional.isPresent()) { System.out.println("不为空"); }
结果:不为空
3、Optional.ifPresent()方法(判读是否为空并返回函数)
这个意思是如果对象非空,则运行函数体 源码:
public void ifPresent(Consumer<? super T> consumer) { if (value != null) consumer.accept(value); }
上面的代码可以简化如下:
Person person = new Person(); person.setAge(2); Optional.ofNullable(person).ifPresent(p-> System.out.println(p.getAge()));
结果:2
4、Optional.filter()方法(过滤对象)
filter()方法大致意思是,接受一个对象,然后对他进行条件过滤,如果条件符合则返回Optional对象本身,如果不符合则返回空Optional
源码:
public Optional<T> filter(Predicate<? super T> predicate) { Objects.requireNonNull(predicate); //如果为空直接返回this if (!isPresent()) return this; else //判断返回本身还是空Optional return predicate.test(value) ? this : empty(); }
简单实例:
Person person = new Person(); person.setAge(2); Optional<Person> optional = Optional.ofNullable(person).filter(person1 -> person1.getAge() > 1); optional.ifPresent(p -> System.out.println(p.getAge()));
结果:2
5、Optional.map()方法(对象进行二次包装)
map()方法将对应Funcation函数式接口中的对象,进行二次运算,封装成新的对象然后返回在Optional中 源码:
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) { Objects.requireNonNull(mapper); //如果为空返回自己 if (!isPresent()) return empty(); else { //否则返回用方法修饰过的Optional return Optional.ofNullable(mapper.apply(value)); } }
例子
Person person = new Person(); person.setAge(2); Integer integer = Optional.ofNullable(person).map(Person::getAge).orElse(null); System.out.println(integer);
结果:2
6、Optional.orElse()方法(为空返回对象)
常用方法之一,这个方法意思是如果包装对象为空的话,就执行orElse方法里的value,如果非空,则返回写入对象 源码:
public T orElse(T other) { //如果非空,返回value,如果为空,返回other return value != null ? value : other; }
7、Optional.orElseGet()方法(为空返回Supplier对象)
这个与orElse很相似,入参不一样,入参为Supplier对象,为空返回传入对象的.get()方法,如果非空则返回当前对象 源码:
Person person = new Person(); person.setAge(2); Optional<Supplier<Person>> sup=Optional.ofNullable(Person::new); //调用get()方法,此时才会调用对象的构造方法,即获得到真正对象 Person person1 = Optional.ofNullable(person).orElseGet(sup.get()); System.out.println(person1);
结果:Person(name=null, age=2)
Person person = null; Optional<Supplier<Person>> sup=Optional.ofNullable(Person::new); //调用get()方法,此时才会调用对象的构造方法,即获得到真正对象 Person person1 = Optional.ofNullable(person).orElseGet(sup.get()); System.out.println(person1);
结果:Person(name=null, age=null)
8、Optional.orElseThrow()方法(为空返回异常)
这个我个人在实战中也经常用到这个方法,方法作用的话就是如果为空,就抛出你定义的异常,如果不为空返回当前对象,在实战中所有异常肯定是要处理好的,为了代码的可读性。源码:
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { if (value != null) { return value; } else { throw exceptionSupplier.get(); } }
实例:这个就贴实战源码了
Person person = null; Optional.ofNullable(person).orElseThrow(() -> new RuntimeException("没有查询到相关数据"));
结果:
java.lang.RuntimeException: 没有查询到相关数据
三、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); // 使用Optional包装 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 作为字段类型。