1.Optional Optional是Java8提供的为了解决null安全问题的一个API。善用Optional可以使代码中很多繁琐、丑陋的设计变得十分优雅。 java8之所以要提出这个对象,是因为java8中主要引入了lambda表达式,这种函数式编程中大量的链式调用,如果用原始的方法去判断nullpointException,会破坏lambda这种风格。
比如在Android开发中,接口json中,对象里面有对象,对象中有属性。每次使用或作为参数传参时,都要!=null非空判断。一个还好,当属性嵌套得非常深的时候,就会出现以下代码。 public class Person { private int age; private String name; private Address address;
public Person(int age, String name, Address address) { this.age = age; this.name = name; this.address = address; }
//…省略set、get方法
public static class Address { private String roomNum; private String areaNum; public Address(String roomNum, String areaNum) { this.roomNum = roomNum; this.areaNum = areaNum; } //…省略set、get方法 } }
//错误码 String errorNum = “error”; if (barry.getAddress() != null && barry.getAddress().getAreaNum() != null && !barry.getAddress().getAreaNum().equals(errorNum)) { String areaNum = barry.getAddress().getAreaNum(); //最后使用areaNum属性做事情 System.out.println("areaNum = " + areaNum); } 总之,就是不好看,臃肿,也不优雅。
Java8中,为了解决以上问题,引入了Optional类。Optional是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
2.Optional中的方法 ①创建Optional Optional有三个静态构造方法: (1) Optional.of(T value) public static < T> Optional< T> of(T value) { return new Optional<>(value); } 该方法通过一个非 null 的 value 来构造一个 Optional,返回的 Optional 包含了 value 这个值。对于该方法,传入的参数一定不能为 null,否则便会抛出 NullPointerException。
举例: Optional< String> op1 = Optional.of(“Hello”); System.out.println(op1.isPresent()); // 输出 true System.out.println(op1.get()); // 输出 Hello Optional< String> op2 = Optional.of(null); // 抛出异常
(2) Optional.ofNullable(T value) public static < T> Optional< T> ofNullable(T value) { return value == null ? empty() : of(value); } 该方法和 of 方法的区别在于,传入的参数可以为 null 。从源码内部可以看出,该方法会判断传入的参数是否为 null,如果为 null 的话,返回的就是 Optional.empty()。
举例: Optional< String> op2 = Optional.ofNullable(null); System.out.println(op2.isPresent()); // 输出 false
(3) Optional.empty() public static< T> Optional< T> empty() { @SuppressWarnings(“unchecked”) Optional< T> t = (Optional< T>) EMPTY; return t; } 该方法用来构造一个空的 Optional,即该 Optional 中不包含值 。其实底层实现还是 如果 Optional 中的 value 为 null ,则该 Optional 为不包含值的状态,然后在 API 层面将 Optional 表现的不能包含 null值,使得 Optional 只存在 包含值 和 不包含值 两种状态。
②isPresent() 判断Optional中包裹的值,如果不为null,返回true,为null返回false。
举例: (1) Optional< Object> optional = Optional.ofNullable(null); if (optional.isPresent()) { Object value = optional.get(); //做操作 System.out.println(value); }
(2)ifPresent() 如果实例非空,调用 Comsumer Lambda 表达式: Optional< String> op1 = Optional.of(“Hello”); op1.ifPresent((s) -> { System.out.println(s); // 输出 Hello });
③Optional.get() 获取Optional中包裹的值,如果值为null,抛出NoSuchElementException异常,一般会配合isPresent()进行判断后,再使用get获得值
举例: (1)Optional< String> op1 = Optional.of(“Hello”); System.out.println(op1.isPresent()); // 输出 true System.out.println(op1.get()); // 输出 Hello (2)Optional< String> optional = Optional.ofNullable(“wally”); if (optional.isPresent()) { String value = optional.get(); //做操作… }
④orElse 判断Optional中包裹的值,如果值为null,返回orElse()中传入的默认值。
举例: Optional< String> op1 = Optional.of(“Hello”); System.out.println(op1.orElse(“World”)); // 输出 Hello Optional< String> op2 = Optional.ofNullable(null); System.out.println(op2.orElse(“World”)); // 输出 World
⑤orElseGet(Supplier<? extends T> other) 如果实例非空,返回该实例,否则返回 Supplier
举例: Optional< String> op1 = Optional.of(“Hello”); System.out.println(op1.orElseGet(() -> {return new String(“World”);})); // 输出 Hello Optional< String> op2 = Optional.ofNullable(null); System.out.println(op2.orElseGet(() -> {return new String(“World”);})); // 输出 World
⑥orElseThrow() 判断Optional中包裹的值,如果为null,抛出Supplier接口中get()返回的Throwable异常对象。否则返回Optional中包裹的值。
举例: try { //对象方式 Object value = optional.orElseThrow(new Supplier< Throwable>() { @Override public Throwable get() { return new NullPointerException(“value is null”); } }); //lambda方式 Object value = optional.orElseThrow((Supplier< Throwable>) () -> new NullPointerException(“value is null”)); //输出 System.out.println("value: " + value); } catch (Throwable throwable) { throwable.printStackTrace(); }
⑦map(Function f) 判断Optional中包裹的值,如果不为null,调用Function接口的apply()方法,获得新值,生成新的一个Optional并包裹新值。如果为null,则返回一个null值的Optional。
举例: Optional< String> newOptional = Optional.of(“hello”).map(new Function<Object, String>() { @Override public String apply(Object o) { return “hi”; } }); if (newOptional.isPresent()) { String value = newOptional.get(); System.out.println(value); }
⑧flatMap(Function mapper) flatMap和map类似,但是Function接口的apply方法返回的是Optional对象。注意apply方法返回的Optional中包裹的值不能为null,否则抛出NullPointerException异常。
举例: Optional< String> optional = Optional.of(“oh no”).flatMap(new Function<String, Optional< String>>() { @Override public Optional< String> apply(String s) { return Optional.of(“oh shit”); } }); optional.ifPresent(System.out::println);
⑨filter 对Optional中包裹的值,做过滤操作,调用Predicate中的test方法,做过滤操作判断,如果返回true,返回原始的Optional,否则返回一个null值的Optional。配合ifPresent()判断,过滤成功则不会调用输出方法。
举例: Optional< String> stringOptional = Optional.of(“wally”).filter(new Predicate< String>() { @Override public boolean test(String value) { return value.length() > 3; } }); stringOptional.ifPresent(System.out::println);
3.Optional的使用 一般,我们判断对象null的代码如下: public static String getName(User u) { if (u == null) return “Unknown”; return u.name; } 有了optional后,我们千万不要改写成下面这种方式: 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的姿势。那么按照这种思路,我们可以安心的进行链式调用,而不是一层层判断了。
注意:要使用Optional,必须将所有属性都用Optional包一次,所以文章开头我们的Person类的Model就要改一下: public class PersonWithOptional { private Optional< Integer> age; private Optional< String> name; private Optional< Address> address;
public PersonWithOptional(Optional< Integer> age, Optional< String> name, Optional< Address> address) { this.age = age; this.name = name; this.address = address; }
public static class Address { private Optional< String> roomNum; private Optional< String> areaNum; public Address(Optional< String> roomNum, Optional< String> areaNum) { this.roomNum = roomNum; this.areaNum = areaNum; } //…省略set、get方法 } //…省略set、get方法 }
Optional.of(barry).flatMap(new Function<PersonWithOptional, Optional<PersonWithOptional.Address>>() { @Override public Optional<PersonWithOptional.Address> apply(PersonWithOptional person) { //取出Address属性对象 return barry.getAddress(); } }).flatMap(new Function<PersonWithOptional.Address, Optional< String>>() { @Override public Optional< String> apply(PersonWithOptional.Address address) { //取出Address属性对象中的areaNum属性 return address.getAreaNum(); } }).filter(new Predicate< String>() { @Override public boolean test(String areaNum) { //校验areaNum的合法性,不合法在下面的ifPresent时被抛弃掉 return !areaNum.equals(errorNum); } }).ifPresent(new Consumer< String>() { @Override public void accept(String areaNum) { //最后使用areaNum属性做事情 System.out.println("areaNum = " + areaNum); } }); 可能有人会说,你这不是纯属增加代码量吗,这比传统写法写多好多代码呀!对于这种情况,终究还是那句话,代码是人看的,代码太复杂不利于阅读,尤其是那种写完几个星期后,只有上帝才知道是什么的代码~况且我们还有lambda这个大杀器! 使用lambda,省略了内部类,是不是代码量少了,也依然结构清晰~ Optional.of(barry) .flatMap(person -> barry.getAddress()) .flatMap(PersonWithOptional.Address::getAreaNum) .filter(areaNum -> !areaNum.equals(errorNum)) .ifPresent(areaNum -> System.out.println("areaNum = " + areaNum));
|