背景:
1965年,英国一位名为Tony Hoare的计算机科学家在设计ALGOL W语言时候提出了null引用的想法,ALGOL W是第一批在堆上分配记录类型的语言之一,Hoare选择用null这种代替空引用,其理由是简单,但是后来的很多语言都采用了类似的设计方式,比如我们熟悉的Java,但是这种方式在开发中为我们带来了巨大的麻烦,我们几乎无时不刻要判断某个对象是否为空,然后才能对其做一些其他的操作。
常规操作:
例子
新建三个类:分别是Person,Car,Insurance,他们的关系是依赖
Insurance依赖Car依赖Person
Insurance:
package com.tangbaobao.java8.option;
import lombok.Getter;
/**
* @author tangxuejun
* @version 2018/9/28 10:13 AM
*/
@Getter
public class Insurance {
private String InsuranceName;
private double price;
}
Car:
package com.tangbaobao.java8.option;
import lombok.Getter;
/**
* @author tangxuejun
* @version 2018/9/28 10:12 AM
*/
@Getter
public class Car {
private String carName;
private Insurance insurance;
}
Person:
package com.tangbaobao.java8.option;
import lombok.Getter;
import java.util.Optional;
/**
* @author tangxuejun
* @version 2018/9/28 10:12 AM
*/
@Getter
public class Person {
private name;
private Car car;
}
出现NullPointException
public class {
public static void main(String ...){
Person p;
System.out.println(p.getCar());
}
}
这是因为调用了一个未在堆上分配内存空间的空引用,即person 为null
解决办法:
通常我们在拿取形参的时候都会判断这个参数是否不为null,类似这样:
public void displayPerson(Person person){
if(person !=null){
System.out.println(person);
}
}
**如果是要调用最底层的类型,则要判断很多层,类似这样:
public void dispayInsuranceName(Person person){
if(person !=null){
if(person.getCar !=null){
if(person.getCar.getInsurance()!=null){
System.out.println(person.getCar.getInsurance().getInsuranceName());
}
}
}
}
如果对象很多层是不是看着很挫?
Optional操作:
汲取了其他语言对null的处理,在java8中引入了java.util.Optional<T>
类来对对象做一些简单的封装,Optional会将对象包进去,如果对象为空则用Optional提供的静态方法Optional.empty();返回一个空的Optional对象;
重构Person,Car,Insurance类
Insurance:
package com.tangbaobao.java8.option;
import lombok.Getter;
/**
* @author tangxuejun
* @version 2018/9/28 10:13 AM
*/
@Getter
public class Insurance {
//对于Insurance来说,它的名称和价格都不应该不存在,所以不用Optional容器来包装
private String InsuranceName;
private double price;
}
Car:
package com.tangbaobao.java8.option;
import lombok.Getter;
/**
* @author tangxuejun
* @version 2018/9/28 10:12 AM
*/
@Getter
public class Car {
private String carName;
//汽车可能没有保险,所以用Optional容器来包装
private Optional<Insurance> insurance;
}
Person:
package com.tangbaobao.java8.option;
import lombok.Getter;
import java.util.Optional;
/**
* @author tangxuejun
* @version 2018/9/28 10:12 AM
*/
@Getter
public class Person {
private name;
//人可能没有汽车,所以也用Optional容器来包装
private Optional<Car> car;
}
重构获取保险名称的代码
在Option类中同样提供了map(Function)方法来对Optional容器做一些操作:
//获取Car
Optional<Person> person = Optional.empty();
Optional<Optional<Car>> car = person.map(Person::getCar);
//获取保险名称
Optional<Insurance> insurance = Optional.empty();
Optional<String> s = insurance.map(Insurance::getInsuranceName);
如上所示,可以用map方法获取Optional中的对象,但是获取的对象还是为Optional类型,所以要将上一层的Optiona去除,这时我们要用到流的扁平化中用到的flatMap,它可以将多个流中扁平化达到我们的效果;
Optional<Person> person = Optional.empty();
Optional<String> insuraceName = person.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getInsuranceName);
如果Person,Car,Insurance中的一个为空,都会返回一个空的Optional对象,不会报空指针异常,这样是不是很优雅了呢?
两个Optional对象组合:
public Insurance findChecpestInsurance(Optional<Car> car,Optional<Person> person){
if(car.isPresent()&&person.isPresent()){
return dosomething(p.get(),car.get());
}
return Optional.empty();
}
private Insurance dosomething(Person p, Car c) {
return null;
}
以上的代码是不是很像判断null一样呢?接下来给出更加优雅的写法:
public Optional<Insurance> findChecpestInsurance(Optional<Person> person, Optional<Car> car) {
return person.flatMap(p -> car.map(c -> dosomething(p, c)));
}
private Insurance dosomething(Person p, Car c) {
return null;
}
如果其中一个为空,都不会往下执行;
Optional特性:
操作了map和flatMap()是不是觉得和Stream有些类似?的确,你可以将Optional理解为最多含有一个元素的Stream。
它还提供了过滤一些元素的方法:
//过滤名称为人寿保险的公司
public void filterByName() {
Optional<Insurance> insurance = Optional.of(new Insurance());
Optional<String> name = insurance
.filter(x -> x.getInsuranceName().equals("人寿保险"))
.map(Insurance::getInsuranceName);
}