我们在编写代码的时候出现最对的就是空指针异常。所以我们需要在各种情况下做非空判断。
List<Author> authors = StreamDemo.getAuthors(); if (CollectionUtils.isNotEmpty(authors)) { Author author = authors.get(0); if (null != author) { System.out.println(author.getName()); } }
尤其对象中的属性还是一个对象时,这种判断会更多。而过多的判断语句让我们的代码显得臃肿不堪。
所以JDK8引入了Optional,养成了使用Optional的习惯后你可以写出更优雅的代码来避免空指针异常。
并且在很多函数式编程相关的API也用到了Optional。
基础方法
public static Author getAuthor() { Author author = new Author(1L, "蒙多", 33, "测试人1", null); return author; } public static Optional<Author> getAuthorOptional() { Author author = new Author(1L, "蒙多", 33, "测试人1", null); return Optional.ofNullable(author); }
Optional就好像是包装类,可以把我们的具体数据封装到Optional对象内部。然后我们去使用Optional中封装好的方法操作数据就可以非常优雅的避免空指针。
// 例子:我们一般使用Optional的静态方法ofNullable来把数据封装成Optional对象,传入的参数是否为null都不会出现问题 Author author = getAuthor(); Optional<Author> authorOptional = Optional.ofNullable(author); authorOptional.ifPresent(authorBean -> System.out.println(authorBean)); // 一般情况都是返回Optional对象的 优化后 Optional<Author> authorOptional1 = getAuthorOptional(); authorOptional1.ifPresent(authorBean -> System.out.println(authorBean)); // 在实际开发中我们的数据大都是从数据库中获取的,Mybatis从3.5版本开始已经可以支持Optional了。 // 我们可以直接把dao方法的返回值类型定义成Optional类型,Mybatis会自己把数据封装成Optional对象返回,封装过程不需要我们自己来操作。 // 如果你确定一个对象不是空的则可使用Optional的静态方法of来把数据封装成Optional对象。 Author author1 = new Author(); Optional<Author> author1Optional = Optional.of(author1); // 一定要注意,使用of的时候传入的参数必须不为null(会出现空指针异常)。 // 如果一个方法的返回值类型是Optional类型,而如果判断发现返回值为null,则需要把null封装成Optional对象返回,调用Optional的静态方法empty()。 Optional.empty();
我们获取到一个Optional对象后肯定需要对其中的数据进行使用,这时候我们可以用ifPresent方法来消费其中的值。
这个方法会判断其内封装的数据是否为空,不为空时才会执行具体的消费代码,这样使用起来就更加安全。
// 例子 Author author = getAuthor(); Optional<Author> authorOptional = Optional.ofNullable(author); authorOptional.ifPresent(authorBean -> System.out.println(authorBean));
如果我们想获取值自行处理可以使用get方法获取,但是不推荐。因为Optional内部的数据为空是会出现异常。
// 例子 Optional<Author> authorOptional = getAuthorOptional(); Author author1 = authorOptional.get();
如果我们想安全获取值,不推荐使用get方法,而是使用Optional提供的一下方法。
// orElseGet // 获取数据并设置如果数据为空时的默认值,如果数据不为空就可以获取到,如果数据为空时则根据你传入的参数来创建对象作为默认值返回。 Optional<Author> authorOptional = getAuthorOptional(); Author author = authorOptional.orElseGet(() -> new Author()); // orElseThrow // 获取数据,如果数据不为空就能获取到该数据,如果为空则根据你传入的参数来创建异常抛出 try { authorOptional.orElseThrow((Supplier<Throwable>) () -> new RuntimeException("author为空。")); } catch (Throwable throwable) { throwable.printStackTrace(); }
我们可以使用filter方法对数据进行过滤,如果原本是有数据的,但是不符合判断,也会变成一个无数据的Optional对象。
Optional<Author> authorOptional = getAuthorOptional(); authorOptional.filter(author -> author.getAge() > 100) .ifPresent(author -> System.out.println(author.getName()));
我们可以使用isPresent方法进行是否存在数据的判断。如果为空返回值为false。不为空返回true。
更推荐使用ifPresent方法
Optional<Author> authorOptional = getAuthorOptional(); authorOptional.filter(author -> author.getAge() > 100) .ifPresent(author -> System.out.println(author.getName()));
optional还提供了map可以让我们对数据进行转换,并且转换得到的数据还是被Optional包装好的,保证了我们的使用安全。
更推荐使用ifPresent方法
// 例如:获取作家的书籍集合 Optional<Author> authorOptional = getAuthorOptional(); Optional<List<Book>> optionalBooks = authorOptional.map(author -> author.getBooks()); optionalBooks.ifPresent(books -> books.forEach(book -> System.out.println(book)));