一、Java8之前访问对象的属性或方法
Java8之前访问对象的属性或方法,会使用到以下方式,以下方式可能会造成空指针异常
String code = user.getAddress().getCountry().getCode().toUpperCase();
在上面的代码中,如果要确保不发生空指针异常必须使用非空判断,如下所示
if (null!=user) { Address address = user.getAddress(); if (null!=address ) { Country country = address.getCountry(); if (null!=country ) { String code = country.getCode(); if (null!=code) { code = code.toUpperCase(); } } } }
代码变得很冗长,难以维护。并且如果再新增加属性,将会增加更多的if判断。为了简化这个过程,我们来看看用 Optional 类是怎么做的。从创建和验证实例,到使用其不同的方法,并与其它返回相同类型的方法相结合,下面是见证 Optional 奇迹的时刻。
二、创建Optional对象
1. 创建包含空值的Optional
Optional.empty();
Optional.of()/Optional.ofNullable()
你可以使用Optional.of()或ofNullable()方法创建Optional对象,两个方法的区别是,使用Optional.of()方法创建对象时,当参数为null时,调用get()方法,程序会抛出NullPointerException。如下图所示
使用Optional.ofNullable(),当参数为null时,程序会抛出java.util.NoSuchElementException: No value present
异常。如下图所示
三、获取Optional对象的值
1. get()方法
使用get()方法,此方法在值为null时,抛出异常
public void getOptionValue(){ String name = "John"; Optional<String> opt = Optional.ofNullable(name); System.out.println(opt.get()); }
2. isPresent()
判断是否有值,再进行后续操作
public void getOptionValue(){ String name = "John"; Optional<String> opt = Optional.ofNullable(name); if(opt.isPresent()){ System.out.println(opt.get()); } }
3. ifPresent()
判断是否有值再进行后续操作,接受一个Consumer(消费者) 参数,如果对象不是空的,就对执行传入的 Lambda 表达式
public void getOptionValue(){ String name = "John"; Optional<String> opt = Optional.ofNullable(name); opt.ifPresent(System.out::println ); }
四、返回默认值
1. orElse()
orElse()方法,它的工作方式非常直接,如果有值则返回该值,否则返回传递给它的参数值。如下示例代码,如果name为null则返回"Sim",如果name不为空则直接返回name。
public void getOptionOrElse(){ String name = null; Optional<String> opt = Optional.ofNullable(name); String nameStr= opt.orElse("Sim"); System.out.println(nameStr); }
2.orElseGet()
此方法略有不同。这个方法会在有值的时候返回值,如果没有值,它会执行作为参数传入的 Supplier(供应者) 函数式接口,并将返回其执行结果:
public void getOptionOrElseGet(){ User user =null; User user2 = new User(); user2.setName("abc"); User user3= Optional.ofNullable(user).orElseGet( () -> user2); System.out.println(user3); }
3.orElse和orElseGet区别
当Optional中对象为空是,两者没有区别都,返回默认值或供给函数提供的值。
当Optional中对象不为空,orElse仍然执行产生默认值的方法,如下图所示:
当Optional中对象不为空,orElseGet不会执行产生默认值的方法,如下图所示:
五、返回异常
1.orElseThrow()
当Optional对象值为空的时抛出异常,异常可以自定义。
public void getOptionOrElseThrow(){ User user2 = null; //当user2为空时,抛出异常 User user3= Optional.ofNullable(user2).orElseThrow( () -> new IllegalArgumentException() ); }
六、转换值
1.map方法
map方法接收一个function接口,并且把返回值包装成Optional对象,使对返回值进行链式调用的操作成为可能,如下代码所示,调用map后,可紧着调用orElse
public void testMap(){ User user2 = new User(); user2.setName("Lisa"); user2.setAddress("上海"); String address = Optional.ofNullable(user2). map(u -> u.getAddress()).orElse("北京"); System.out.println(address); }
2.flatMap
map方法接收一个function接口,返回值直接是一个Optional对象,不需要进行重新包装。
public Optional<String> getCode(){ return Optional.ofNullable(code); } public void testFlatMap(){ User user2 = new User(); user2.setName("Lisa"); user2.setAddress("上海"); String code = Optional.ofNullable(user2).flatMap(u -> u.getCode()).orElse("北京"); System.out.println(code); }
七、过滤值
Optional 类也提供了按条件“过滤”值的方法,filter() 接受一个 Predicate 参数,返回测试结果为 true 的值。如果测试结果为 false,会返回一个空的 Optional。
public void testFilter() { User user = new User(); user.setEmail("anna@gmail.com"); Optional<User> result = Optional.ofNullable(user) .filter(u -> StringUtils.isNotEmpty(u.getEmail()) && u.getEmail().contains("@")); System.out.println(result); }
八、怎么使用Optional
1.使用场景
Optional 主要用作返回类型。在获取到这个类型的实例后,如果它有值,你可以取得这个值,否则可以进行一些替代行为。
Optional 类有一个非常有用的用例,就是将其与流或其它返回 Optional 的方法结合,以构建流畅的API。
2.不适合使用场景
Optional 不是 Serializable。因此,它不应该用作类的字段。如果你需要序列化的对象包含 Optional 值,Jackson 库支持把 Optional 当作普通对象。也就是说,Jackson 会把空对象看作 null,而有值的对象则把其值看作对应域的值。这个功能在 jackson-modules-java8 项目中。
它在另一种情况下也并不怎么有用,就是在将其类型用作方法或构建方法的参数时,这样做会让代码变得复杂,完全没有必要。比如以下代码
User user = new User(23, "Lisa", Optional.empty());
九、总结
Optional主要用来解决空指针异常,结合Stream流及Lambda表达式,使应用程序更加优化及健壮。