如何为缺失的值建模
原始的两种方式:
Java8中引入了Optional类。java.util.Optional。
变量存在时,Optional类只是对类简单封装。变量不存在时,缺失的值会被建模成一个“空” 的Optional对象,由方法Optional.empty()返回。
Optional.empty()方法是一个静态工厂方法,它返回Optional类的特定单一实例。null和Optional.empty()没啥区别,就是防止出发nullException。
代码中person引用的是Optional, 而car引用的是Optional,这种方式非常清 地表达了你的模型中一个person 可能 有也可能没有car的情形,同样,car可能进行了保险,也可能没有保险。
使用Optional
- 声明一个空的Optional
Optional<Car> optCar = Optional.empty();
- 根据一个非空值创建Optional
Optional<Car> optCar = Optional.of(car);
如果car是一个null,这段代码会立即抛出一个NullPointerException,而不是等到你试图访问car的属性值时才返回一个错误。 - 可接收null的Optional
Optional<Car> optCar = Optional.ofNullable(car);
如果car是null,那么得到的Optional对象就是个空对象。
使用map从Optional对象中提取和转换值
map操作会将提供的函数应用于流的每个元素。Optional对象可以看成特殊的集合数据,它至多包含一个元素。如果Optional包含一个值,那函数就将该值作为参数传递给map,对该值进行转换。如果Optional为空,那么什么也不做。
使用flatMap链接optional对象
看看下图代码如何应用Optional
我们可能会使用刚才提到的map方法
Optional<Person> optPerson = Optional.of(person);
Optional<String> name =
optPerson.map(Person::getCar)
.map(Car::getInsurance)
.map(Insurance::getName);
不幸的是,这段代码通过不了。getCar方法返回的是Optional<Car>
类型。Car::getInsurance
方法就无法执行了。因为Optional<Car>
没有getInsurance方法。
这是就要用到flatMap函数,该方法接收一个函数作为参数,这个函数的返回值是另一个流。这个方法会应用到流中的每一个元素,最终形成一个新的流的流。但是flagMap会用流的内容替换每个新生成的流。换句话说,由方法生成的各个流会被合并或者扁平化为一个单一的流。
传递给optional的flatMap方法的函 数会将原始包含正方形的optional对象转换为包含三角形的optional对象。如果将该方法传递 给map方法,结果会是一个Optional对象,而这个Optional对象中包含了三角形;但flatMap 方法会将这种两层的Optional对象转换为包含三角形的单一Optional对象。
至目前为止,返回的Optional可能是两种情况:如果调用链上的任何一个方法返回一个 空的Optional,那么结果就为空,否则返回的值就是你期望的保险公司的名称。
如果一定要实现序列化域模型可以如下图这样
让我们看看Optional内部的方法
get()
是这些方法中最简单但又最不安全的方法。如果变量存在,它直接返回封装的变量值,否则就抛出一个NoSuchElementException异常。
orElse(T other)
它允许你在Optional对象不包含值时提供一个默认值。
orElseGet(Supplier<? extends T> other)
是orElse方法的延迟调用版,Supplier 方法只有在Optional对象不含值时才执行调用。
orElseThrow(Supplier<? extends X> exceptionSupplier)
和get方法非常类似, 它们 遇Optional对象为空时都会 出一个异常,但是使用orElseThrow你可以定制希 望 出的异常类型。
ifPresent(Consumer<? super T>)
让你能在变量值存在时执行一个作为参数传入的 方法,否则就不进行任何操作。
empty
返回一个空的 Optional 实例
filter
如果值存在并且满足提供的谓词,就返回包含该值的 Optional 对象;否则返回一个空的 Optional 对象
of
将指定值用 Optional 装之后返回,如果该值为 null,则 出一个 NullPointerException 异常
get
如果该值存在,将该值用 Optional 装返回,否则 出一个 NoSuchElementException 异常
public Optional<Insurance> nullSafeFindCheapestInsurance(
Optional<Person> person, Optional<Car> car) {
if (person.isPresent() && car.isPresent()) {
}
return;
}
如果还是这样判断那么和判null没什么区别。可以看下图这样。
用filter剔除特定值
Optional<Insurance> optInsurance = ...;
optInsurance.filter(insurance ->
"CambridgeInsurance".equals(insurance.getName()))
.ifPresent(x -> System.out.println("ok"));
filter方法接受一个谓词作为参数。如果Optional对象的值存在,并且它符合谓词的条件, filter方法就返回其值;否则它就返回一个空的Optional对象。如果你还记得我们可以将 Optional看成最多包含一个元素的Stream对象,这个方法的行为就非常清 了。如果Optional对象为空,它不做任何操作,反之,它就对Optional对象中包含的值 加谓词操作。如果该操作的结果为true,它不做任何改变,直接返回该Optional对象,否则就将该值过 ,将 Optional的值空。
常规使用
Optional<Object> value = Optional.ofNullable(map.get("key"));
public static Optional<Integer> stringToInt(String s) {
try {
return Optional.of(Integer.parseInt(s));
} catch (NumberFormatException e) {
return Optional.empty();
}
}
public int readDuration(Properties props, String name) {
return Optional.ofNullable(props.getProperty(name))
.flatMap(OptionalUtility::stringToInt)
.filter(i -> i > 0)
.orElse(0);
}