Java8特性之Optional
一、什么是Optional
Optional从名字上看,意思应该是可选项的意思,那么在Java8的实际使用中,我们就是使用它来表示一个可以为空的对象,给开发者提供了一种可以不用判断非空的程序写法,更加地易读和优雅。
比如,我们创建两个方法,分别用来获取空对象和非空对象:
private Person getNullPerson(){
return null;
}
private Person getPerson(){
return new Person("mason",22, 3500.0);
}
如果我们不使用Optional,那么我们可能会写出这样的代码:
@Test
public void test01(){
Person person = getNullPerson();
// 直到使用的时候才报出NPE
log.info("员工的姓名为:{},年龄为:{},薪水为:{}", person.getName(), person.getAge(), person.getSalary());
}
这段代码很容易就出现NPE,所以我们一般这样写:
@Test
public void test01(){
Person person = getNullPerson();
if(person != null){
// 每次使用都需要进行非空判断,非常繁琐,容易遗忘
log.info("员工的姓名为:{},年龄为:{},薪水为:{}", person.getName(), person.getAge(), person.getSalary());
}
}
可以看到,这样写比较繁琐,我们完全可以使用Optional来完成这个例子:
@Test
public void test03(){
// 提前报出NPE问题,而不是等到使用的时候才发现
Optional<Person> optional = Optional.of(getNullPerson());
Person person = optional.get();
log.info("员工的姓名为:{},年龄为:{},薪水为:{}", person.getName(), person.getAge(), person.getSalary());
}
在如上的例子中,只要Optional.of
的对象是空的,那么就会提前抛出NPE问题,而不是等到使用的时候才发现。
这么一看,代码好像也没有变得更加精简,Optional也没有体现它的优雅和简洁。别着急,我们先来看看Optional的一些常规用法。
二、Optional的使用
-
of的用法;
在上面的案例中,我们就已经使用了该方法,获取一个Optional<T>的对象,倘若对象为空,则of方法直接就抛出NPE;对象不为空的话,我们可以通过get来获取该非空T对象;
@Test public void test02(){ // 正常的使用 Optional<Person> optional = Optional.of(getPerson()); Person person = optional.get(); log.info("员工的姓名为:{},年龄为:{},薪水为:{}", person.getName(), person.getAge(), person.getSalary()); }
-
empty的用法;
我们可以通过调用empty来获取一个空的Optional<T>对象,如果直接使用该空对象尝试获取T对象的话,就会抛出
java.util.NoSuchElementException: No value present
,通常情况下,empty只会在声明JavaBean中的属性为空的Optional<T>时使用。@Test public void test04(){ // 创建一个空的实例 Optional<Person> optional = Optional.empty(); // 如下语句抛出java.util.NoSuchElementException: No value present Person person = optional.get(); log.info("员工的姓名为:{},年龄为:{},薪水为:{}", person.getName(), person.getAge(), person.getSalary()); }
-
ofNullable的用法;
在上面of的用法中,不允许对象为空,而有的时候,我们仍然需要这个为空的Optional<T>对象,那么就可以使用ofNullable。
@Test public void test05(){ // 创建一个可以为空的实例 Optional<Person> optional = Optional.ofNullable(getNullPerson()); // 如下语句抛出java.util.NoSuchElementException: No value present Person person = optional.get(); log.info("员工的姓名为:{},年龄为:{},薪水为:{}", person.getName(), person.getAge(), person.getSalary()); }
-
isPresent的用法;
通常ofNullable和isPresent都是配合使用的。
@Test public void test07(){ Optional<Person> optional = Optional.ofNullable(getNullPerson()); if(optional.isPresent()){ Person person = optional.get(); log.info("员工的姓名为:{},年龄为:{},薪水为:{}", person.getName(), person.getAge(), person.getSalary()); } }
-
orElse的用法;
如果我们使用isPresent,仿佛又回到了
!=null
的老路子上去了,所以,我们还可以在Optional<T>为空的时候,指定一个默认的非空对象。@Test public void test10(){ // 创建一个可以为空的实例 Person person = Optional.ofNullable(getNullPerson()).orElse(getDefaultPerson()); log.info("员工的姓名为:{},年龄为:{},薪水为:{}", person.getName(), person.getAge(), person.getSalary()); } private Person getDefaultPerson(){ return new Person("default",20, 3000.0); }
-
orElseGet的用法;
和orElse的作用类似,区别是可以指定额外的处理逻辑。
@Test public void test11(){ // 创建一个可以为空的实例 Person person = Optional.ofNullable(getNullPerson()).orElseGet(() -> new Person("Jack", 33, 5501.1)); log.info("员工的姓名为:{},年龄为:{},薪水为:{}", person.getName(), person.getAge(), person.getSalary()); }
-
map的用法;
我们还可以对T对象进行一些流式处理。
@Test public void test12(){ // 创建一个可以为空的实例 Integer age = Optional.ofNullable(getNullPerson()) .map((x) -> x.getAge()) .map((y) -> y*2) .orElse(45); log.info("员工的年龄为:{}", age); }
三、为什么要使用Optional
我们在日常的使用中,经常会遇到这样的场景:
@Data
public class Home {
private String address;
public Home(){}
public Home(String address){
this.address = address;
}
}
@Data
public class Person {
private String name;
private Integer age;
private Double salary;
private Home home;
public Person(){}
public Person(String name, Integer age, Double salary){
this.name = name;
this.age = age;
this.salary = salary;
}
}
@Test
public void test15(){
Person person = getNullPerson();
if(person != null){
Home home = person.getHome();
if(home != null){
String address = home.getAddress();
log.info("员工的地址为:{}", address);
}
}
}
我们尝试打印Person中的Home中的address属性,不得不做两次非空校验,而且,对于Person和Home任一为空的情况没有做任何的处理,否则,将还会多出来两个else语句,十分繁琐,冗余。
但是如果我们使用Optional会怎样?
@Data
public class Person {
private String name;
private Integer age;
private Double salary;
private Optional<Home> home = Optional.empty();
public Person(){}
public Person(String name, Integer age, Double salary){
this.name = name;
this.age = age;
this.salary = salary;
}
}
@Test
public void test14(){
// 创建一个可以为空的实例
String address = Optional.ofNullable(getNullPerson()).orElse(getDefaultPerson())
.getHome().orElseGet(() -> new Home("China!!!")).getAddress();
log.info("员工的地址为:{}", address);
}
在上面的例子中,我们完全实现了test15的功能,除此之外,还对Person为空和Home为空的情况做了默认值设置,大大提升了易读性,变得更加优雅。