概述:
Optional最早是Google公司Guava中的概念,代表的是可选值。Optional类从Java8版本开始加入豪华套餐,主要为了解决程序中的NPE问题,从而使得更少的显式判空,防止代码污染,另一方面,也使得领域模型中所隐藏的知识,得以显式体现在代码中。Optional类位于java.util包下,对链式编程风格有一定的支持。实际上,Optional更像是一个容器,其中存放的成员变量是一个T类型的value,可值可Null,使用的是Wrapper模式,对value操作进行了包装与设计。本文将从Optional所解决的问题开始,逐层解剖,由浅入深,文中会出现Optioanl方法之间的对比,实践,误用情况分析,优缺点等。与大家一起,对这项Java8中的新特性,进行理解和深入。
1、解决的问题
臭名昭著的空指针异常,是每个程序员都会遇到的一种常见异常,任何访问对象的方法与属性的调用,都可能会出现NullPointException,如果要确保不触发异常,我们通常需要进行对象的判空操作。
举个栗子,有一个人(Shopper)进超市购物,可能会用购物车(Trolley)也可能会用其它方式,购物车里可能会有一袋栗子(Chestnut),也可能没有。三者定义的代码如下:
P lainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPython
public class Shopper {
private Trolley trolley;
public Trolley getTrolley(){
return trolley;
}
}
public class Trolley {
private Chestnut chestnut;
public Chestnut getChestnut(){
return chestnut;
}
}
public class Chestnut {
private String name;
public String getName(){
return name;
}
}
这时想要获得购物车中栗子的名称,像下面这么写,就可能会见到我们的“老朋友”(NPE)
P lainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPython
public String result(Shopper shopper){
return shopper.getTrolley().getChestnut().getName();
}
为了能避免出现空指针异常,通常的写法会逐层判空(多层嵌套法),如下
P lainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPython
public String result(Shopper shopper) {
if (shopper != null) {
Trolley trolley = shopper.getTrolley();
if (trolley != null) {
Chestnut chestnut = trolley.getChestnut();
if (chestnut != null) {
return chestnut.getName();
}
}
}
return "获取失败辽";
}
多层嵌套的方法在对象级联关系比较深的时候会看的眼花缭乱的,尤其是那一层一层的括号;另外出错的原因也因为缺乏对应信息而被模糊(例如trolley为空时也只返回了最后的获取失败。当然也可以在每一层增加return,相应的代码有会变得很冗长),所以此时我们也可以用遇空则返回的卫语句进行改写。
P lainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPython
public String result(Shopper shopper) {
if (shopper == null) {
return "购物者不存在";
}
Trolley trolley = shopper.getTrolley();
if (trolley == null) {
return "购物车不存在";
}
Chestnut chestnut = trolley.getChestnut();
if (chestnut == null) {
return "栗子不存在";
}
return chestnut.getName();
}
为了取一个名字进行了三次显示判空操作,这样的代码当然没有问题,但是优秀的工程师们总是希望能获得更优雅简洁的代码。Optional就提供了一些方法,实现了这样的期望。
P lainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPython
public String result(Shopper shopper){
return Optional.ofNullable(shopper)
.map(Shopper::getTrolley)
.map(Trolley::getChestnut)
.map(Chestnut::getName)
.orElse("获取失败辽");
}
2、常用方法
1)获得Optional对象
Optional类中有两个构造方法:带参和不带参的。带参的将传入的参数赋值value,从而构建Optional对象;不带参的用null初始化value构建对象。
P lainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPython
private Optional() {}
private Optional(T value) {}
但是两者都是私有方法,而实际上Optional的对象都是通过静态工厂模式的方式构建,主要有以下三个函数
P lainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPython
public static Optional of(T value) {}
public static Optional ofNullable(T value) {}
public static Optional empty() {}
创建一个一定不为空的Optional对象,因为如果传入的参数为空会抛出NPE
P lainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPython
Chestnut chestnut = new Chestnut();
Optional opChest = Optional.of(chestnut);
创建一个空的Optional对象
P lainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPython
Optional opChest = Optional.empty();
创建一个可空的Optional对象
P lainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPython
Chestnut chestnut = null;
Optional opChest = Optional.ofNullable(chestnut);
2)正常使用
正常使用的方法可以被大致分为三种类型,判断类、操作类和取值类
P lainJavascriptJavaHTML/XMLMarkdownMakefileGoJSONSQLObjective-cYAMLBashPHPPython
//判断类
public boolean isPresent() {}
//操作类
public void ifPresent(Consumer