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类的依赖依然还是函数接口那一套东西:
importjava.util.function.Consumer;importjava.util.function.Function;importjava.util.function.Predicate;import java.util.function.Supplier;
是要把面向接口编程走到底了。私有字段只有一个:
private final T value;
原生代码还是相当简单的:
public static Optionalempty() {
@SuppressWarnings("unchecked")
Optional t = (Optional) EMPTY;returnt;
}public static Optionalof(T value) {return new Optional<>(value);
}public static OptionalofNullable(T value) {return value == null ?empty() : of(value);
}public Optional map(Function super T, ? extends U>mapper) {
Objects.requireNonNull(mapper);if (!isPresent())returnempty();else{returnOptional.ofNullable(mapper.apply(value));
}
}
......
创建 Optional 包装实例
建空
Optional emptyOpt =Optional.empty();try{
Insurance obj=emptyOpt.get();
System.out.println("不为空");
}catch(NoSuchElementException ex) {
System.out.println("为空");
}
尝试访问 emptyOpt 变量的值会导致 NoSuchElementException,因为原声代码:
publicT get() {if (value == null) {throw new NoSuchElementException("No value present");
}returnvalue;
}
建可能为空(ofNullable不抛异常)
Optional opt = Optional.ofNullable(null);
不会异常
如此神奇,原因在于原生代码已经进行了判空,把私有value定成了empty(),这种不抛异常也会产生一个问题,那就是在消费实例的时候,还是要过滤掉空值。
public static OptionalofNullable(T value) {return value == null ?empty() : of(value);
}
建为空时抛异常(即of不可为空)
public static voidwhenCreateOfEmptyOptional_thenNullPointerException() {
Insurance ins= newInsurance();
Optional opt =Optional.of(ins);
ins=null;try{
opt=Optional.of(ins);
System.out.println("不为空");
}catch(NullPointerException ex) {
System.out.println("为空");
}
}
会进入异常。跟踪原生代码发现了原因:
public static T requireNonNull(T obj) {if (obj == null)throw newNullPointerException();returnobj;
}
访问 Optional 对象的值
get
从 Optional 实例中取回实际值对象的方法之一是使用 get() 方法:
@Testpublic voidwhenCreateOfNullableOptional_thenOk() {
String name= "John";
Optional opt =Optional.ofNullable(name);
assertEquals("John", opt.get());
}
这个方法会在值为 null 的时候抛出异常。要避免异常,你可以选择首先验证是否有值:
@Testpublic voidwhenCheckIfPresent_thenOk() {
User user= new User("john@gmail.com", "1234");
Optional 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 voidprintName(Insurance obj)
{
Optional.ofNullable(obj).ifPresent(u-> System.out.println("The name is : " +u.getName()));
}
Insurance obj= newInsurance();
obj.setName("张三");
Insurance objNull= null;
printName(obj);
printName(objNull);//输出:The name is : 张三
isPresent的使用
感觉有了ifPresent,不需要再用isPresent来判断空了,否则感觉非常啰嗦。
orElse的使用
在对象为空的时候返回默认值。它的工作方式非常直接,如果有值则返回该值,否则返回传递给它的参数值。
Insurance obj = newInsurance();
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 voidfilterAge(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 Optional map(Function super T, ? extends U>mapper) {
Objects.requireNonNull(mapper);if (!isPresent())returnempty();else{returnOptional.ofNullable(mapper.apply(value));
}
}
下面代码中,先用ofNullable()方法构造一个Optional对象,然后用map()计算学生的年龄,返回Optional对象(如果student为null, 返回map()方法返回一个空的Optinal对象)
public static OptionalgetAge(Student student)
{return Optional.ofNullable(student).map(u ->u.getAge());
}
flatMap的使用
跟map()方法不同的是,入参Function函数的返回值类型为Optional类型,而不是U类型,这样flatMap()能将一个二维的Optional对象映射成一个一维的对象,
总而言之,map进去是什么,出来维度不变,flatMap是为了拍扁结果。
public Optional flatMap(Function super T, Optional>mapper) {
Objects.requireNonNull(mapper);if (!isPresent())returnempty();else{returnObjects.requireNonNull(mapper.apply(value));
}
}//例子
Optional nonEmptyGender = Optional.of("male");
Optional emptyGender =Optional.empty();
System.out.println("Non-Empty Optional:: " +nonEmptyGender.map(String::toUpperCase));
System.out.println("Empty Optional :: " +emptyGender.map(String::toUpperCase));
Optional> 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 opt = Optional.ofNullable(map.get("someNullKey"));