Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException)。是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空。
在这段代码就可能产生空异常;
String isocode = user.getAddress().getCountry().getIsocode().toUpperCase(); //需要检查: if (user != null) { Address address = user.getAddress(); if (address != null) { Country country = address.getCountry(); if (country != null) { String isocode = country.getIsocode(); if (isocode != null) { isocode = isocode.toUpperCase(); } } } }
Optional类的依赖依然还是函数接口那一套东西:
import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier;
是要把面向接口编程走到底了。私有字段只有一个:
private final T 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); } public static <T> Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); } public<U> Optional<U> map(Function<? super T, ? extends U> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Optional.ofNullable(mapper.apply(value)); } } ......
创建 Optional 包装实例
建空
Optional<Insurance> emptyOpt = Optional.empty(); try { Insurance obj = emptyOpt.get(); System.out.println("不为空"); } catch (NoSuchElementException ex) { System.out.println("为空"); }
尝试访问 emptyOpt 变量的值会导致 NoSuchElementException,因为原声代码:
public T get() { if (value == null) { throw new NoSuchElementException("No value present"); } return value; }
建可能为空(ofNullable不抛异常)
Optional<Insurance> opt = Optional.ofNullable(null);
不会异常
如此神奇,原因在于原生代码已经进行了判空,把私有value定成了empty(),这种不抛异常也会产生一个问题,那就是在消费实例的时候,还是要过滤掉空值。
public static <T> Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); }
建为空时抛异常(即of不可为空)
public static void whenCreateOfEmptyOptional_thenNullPointerException() { Insurance ins = new Insurance(); Optional<Insurance> opt = Optional.of(ins); ins=null; try { opt = Optional.of(ins); System.out.println("不为空"); } catch (NullPointerException ex) { System.out.println("为空"); } }
会进入异常。跟踪原生代码发现了原因:
public static <T> T requireNonNull(T obj) { if (obj == null) throw new NullPointerException(); return obj; }
访问 Optional 对象的值
get
从 Optional 实例中取回实际值对象的方法之一是使用 get() 方法:
@Test public void whenCreateOfNullableOptional_thenOk() { String name = "John"; Optional<String> opt = Optional.ofNullable(name); assertEquals("John", opt.get()); }
这个方法会在值为 null 的时候抛出异常。要避免异常,你可以选择首先验证是否有值:
@Test public void whenCheckIfPresent_thenOk() { User user = new User("john@gmail.com", "1234"); Optional<User> opt = Optional.ofNullable(user); assertTrue(opt.isPresent()); assertEquals(user.getEmail(), opt.get().getEmail()); }
ifPresent的使用
原生代码:
public void ifPresent(Consumer<? super T> consumer) { if (value != null) consumer.accept(value); }
只有当值不为空,才执行消费者方法accept,这是比较安全的。
public static void printName(Insurance obj) { Optional.ofNullable(obj).ifPresent(u -> System.out.println("The name is : " + u.getName())); } Insurance obj = new Insurance(); obj.setName("张三"); Insurance objNull = null; printName(obj); printName(objNull); //输出:The name is : 张三
isPresent的使用
感觉有了ifPresent,不需要再用isPresent来判断空了,否则感觉非常啰嗦。
orElse的使用
在对象为空的时候返回默认值。它的工作方式非常直接,如果有值则返回该值,否则返回传递给它的参数值。
Insurance obj = new Insurance(); obj.setName("张三"); Insurance objNull = null; Insurance result = Optional.ofNullable(objNull).orElse(obj); System.out.println(obj.hashCode()); System.out.println(result.hashCode());
result实际是返回的obj,而不是null。
filter的使用
filter()方法接受参数为Predicate对象,用于对Optional对象进行过滤,如果符合Predicate的条件,返回Optional对象本身,否则返回一个空的Optional对象
public static void filterAge(Student student) { Optional.ofNullable(student).filter( u -> u.getAge() > 18).ifPresent(u -> System.out.println("The student age is more than 18.")); }
map的使用
map()方法的参数为Function(函数式接口)对象,map()方法将Optional中的包装对象用Function函数进行运算,并包装成新的Optional对象(包装对象的类型可能改变)
原生代码:
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Optional.ofNullable(mapper.apply(value)); } }
下面代码中,先用ofNullable()方法构造一个Optional<Student>对象,然后用map()计算学生的年龄,返回Optional<Integer>对象(如果student为null, 返回map()方法返回一个空的Optinal对象)
public static Optional<Integer> getAge(Student student) { return Optional.ofNullable(student).map(u -> u.getAge()); }
flatMap的使用
跟map()方法不同的是,入参Function函数的返回值类型为Optional<U>类型,而不是U类型,这样flatMap()能将一个二维的Optional对象映射成一个一维的对象,
总而言之,map进去是什么,出来维度不变,flatMap是为了拍扁结果。
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Objects.requireNonNull(mapper.apply(value)); } } //例子 Optional<String> nonEmptyGender = Optional.of("male"); Optional<String> emptyGender = Optional.empty(); System.out.println("Non-Empty Optional:: " + nonEmptyGender.map(String::toUpperCase)); System.out.println("Empty Optional :: " + emptyGender.map(String::toUpperCase)); Optional<Optional<String>> nonEmptyOtionalGender = Optional.of(Optional.of("male")); System.out.println("Optional value :: " + nonEmptyOtionalGender); System.out.println("Optional.map :: " + nonEmptyOtionalGender.map(gender -> gender.map(String::toUpperCase))); System.out.println("Optional.flatMap :: " + nonEmptyOtionalGender.flatMap(gender -> gender.map(String::toUpperCase)));
封装可能的空值
使用ofNullable,比如在一些map中的key为空的情况下使用:
Optional<Insurance> opt = Optional.ofNullable(map.get("someNullKey"));