在目前的工做中,我对Java中的Stream和Lambda表达式都使用得不少,以前也写了两篇文章来总结对应的知识。java
不过对于Optional这个特性,一直没有很好地使用起来,因此最近又开始阅读《Java 8实战》这本书,本文是针对其中第10章的一个学习总结。面试
在Java中,若是你尝试对null作函数调用,就会引起NullPointerException(NPE),NPE是Java程序开发中的最典型的异常,对于Java开发者来讲,不管你是初出茅庐的新人和还工做多年的老司机,NPE常常让他们翻车。为了不NPE,他们会加不少if判断语句,使得代码的可读性变得不好。数据库
从软件设计的角度来看,null自己是没有意义的语义,这是一种对缺失变量值的错误的建模。编程
从Java类型系统的角度看,null能够被赋值给任何类型的变量,而且不断被传递,知道最后谁也不知道它是从哪里引入的。后端
Java设计者从Haskell和Scala中获取灵感,在Java 8中引入了一个新的类java.util.Optional<T>
。若是一个接口返回Optional,能够表示一我的可能有车也可能没有车,这个比简单的返回Car要更明确,阅读代码的人不须要提早准备业务知识。设计模式
Optional的目的就在于此:经过类型系统让你的领域模型中隐藏的知识显式地体如今你的代码中。安全
方法 | 描述 |
---|---|
empty | 返回一个空的Optional实例 |
filter | 若是值存在而且知足提供的过滤条件,则返回包含该值的Optional对象;不然就返回一个空的Optional对象 |
map | 若是值存在,就对该值执行提供的mapping函数调用 |
flatMap | 若是值存在,就对该值执行提供的mapping函数调用,返回一个Optional类型的值,不然就返回一个空的Optional对象 |
ifPresent | 若是值存在,就执行使用该值的方法调用,不然什么也不作 |
of | 将指定值用Optional封装以后返回,若是该值为null,则抛出一个NPE |
ofNullable | 将指定值用Optional封装以后返回,若是该值为null,则返回一个空的Optional对象 |
orElse | 若是有值则返回,不然返回一个默认值 |
orElseGet | 若是有值则返回,不然返回一个由指定的Supplier接口生成的值(若是默认值的生成代价比较高的话,则适合使用orElseGet方法) |
orElseThrow | 若是有值则返回,不然返回一个由指定的Supplier接口抛出的异常 |
get | 若是值存在,则返回该值,不然抛出一个NoSuchElementException异常 |
isPresent | 若是值存在则返回true,不然返回false |
上面这张表里列举了Optional的基础API,我这里列举了一些使用的tips:app
//empty方法的使用
Optional<Car> optCar = Optional.empty();
//of方法的使用
Optional<Car> optCar = Optional.of(car);
//ofNullable方法的使用
Optional<Car> optCar = Optional.ofNullable(car);
复制代码
Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);
复制代码
//转换以前
public String getCarInsuranceName(Person person) {
return person.getCar().getInsurance().getName();
}
//转换后
public String getCarInsuranceName(Optional<Person> person) {
return person.flatMap(Person::getCar)
.flatMap(Car::Insurance)
.map(Insurance::getName)
.orElse("Unknown");
}
复制代码
public class Person {
private Car car;
public Optional<Car> getCarAsOptional() {
return Optional.ofNullable(car);
}
}
复制代码
Java方法处理异常结果的方式有两种:返回null(或错误码);抛出异常,例如:Integer.parseInt(String)这个方法——若是没法解析到对应的整型,该方法就抛出一个NumberFormationException,这种状况下咱们通常会使用try/catch语句处理异常状况。函数式编程
通常咱们建议将try/catch块单独提取到一个方法中,在这里使用Optional设计这个方法,代码以下。在开发中,能够尝试构建一个OptionalUtility工具类,将这些复杂的try/catch逻辑封装起来。函数
public static Optional<Integer> stringToInt(String a) {
try{
return Optional.of(Integer.parseInt(s));
} catch (NumberFormationException e) {
return Optional.empty();
}
}
复制代码
如今有个方法,是尝试从一个属性映射中获取某个关键词对应的值,例子代码以下:
public static int readDuration(Properties properties, String name) {
String value = properties.getProperty(name);
if (value != null) {
try {
int i = Integer.parseInt(value);
if (i > 0) {
return i;
}
} catch (NumberFormatException e) {
}
}
return 0;
}
复制代码
使用Optional的写法后,代码以下所示:
public static int readDurationWithOptional(Properties properties, String name) {
return Optional.ofNullable(properties.getProperty(name))
.flatMap(OptionalUtility::stringToInt)
.filter(integer -> integer > 0)
.orElse(0);
}
复制代码
若是须要访问的属性值不存在,Properites.getProperty(String)方法的返回值就是一个null,使用noNullable工厂方法就能够将该值转换为Optional对象;接下来,可使用flatMap将一个Optional转换为Optional对象;最后使用filter过滤掉负数,而后就可使用orElse获取属性值,若是拿不到则返回默认值0。
使用Optional的思路和Stream相同,都是链式思路,跟数据库查询似的,表达力很强,并且省去了哪些复杂的try/catch和if-then-else方法。在后面的开发中,可使用Optional设计API,这样能够设计出更安全的接口和方法。
本号专一于后端技术、JVM问题排查和优化、Java面试题、我的成长和自我管理等主题,为读者提供一线开发者的工做和成长经验,期待你能在这里有所收获。