主要内容:
- 为什么要避免null引用
- 从null到Optional
- 读取Opational中可能值的几种方法
null引用的由来
1965年,英国一位名为Tony Hoare的计算机科学家在设计ALGOL W语言时提出了null引用的想法。Hoare选择null引用这种方式,“只
是因为这种方法实现起来非常容易”。
如何减少NullPointerException
通常可以采用防御式编程来减少NullPointerException。 如:
public String getCarInsuranceName(Person person) {
if (person != null) {
Car car = person.getCar();
if (car != null) {
Insurance insurance = car.getInsurance();
if (insurance != null) {
return insurance.getName();
}
}
}
return "Unknown";
}
很明显,这样的方式牺牲了代码的可读性。我们需要更优雅的解决方案。
Optional<T>入门
汲取Haskell和Scala的灵感,Java 8中引入了一个新的类java.util.Optional。这是一个封装Optional值的类。当变量存在时,Optional类只是对对象的简单封装,当变量不存在时,会封装一个空的Optional对象,由Optional.empty()返回。
-
声明一个空的Optional
Optional<Car> optional = Optional .empty(); -
依据一个非空值创建Optional
Optional<Car> optional = Optional .of(car); -
可接受null的Optional
Optional<Car> optCar = Optional.ofNullable(car);
此种情况下,car可为null,为null时,实际返回的是Optional.empty()
使用map从Optional对象中提取和转换值
示例:
Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);
使用flatMap链接Optional对象
public String getCarInsuranceName(Optional<Person> person) {
return person.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("Unknown");
}
这样的写法不需要增加额外分支,也不会增加代码的复杂性。
获取Optional对象值
- get()
是最简单也最不安全的方法。如果变量存在返回变量,否则抛出NoSuchElementException。 - orElse(T other)
如果变量存在返回变量,不存在返回other。 - orElseGet(Supplier<? extends T> other)
是orElse的延迟调用版 - orElseThrow(Supplier exceptionSupplier)
当值为null时,可以定制抛出的异常 - ifPresent(Consumer<? super T> consumer)
当值存在时执行消费,否则不执行
使用filter剔除特定的值
filter可接受一个谓词,当且仅当值不为空,且满足谓词时才继续执行,否则返回Optional.empty()。 示例:
Optional<Insurance> optInsurance = ...;
optInsurance.filter(insurance -> "CambridgeInsurance".equals(insurance.getName())) .ifPresent(x -> System.out.println("ok"));
综合示例
@Slf4j
public class OptionalSample {
public static void main(String[] args) {
Properties props = new Properties();
props.setProperty("a", "5");
props.setProperty("b", "true");
props.setProperty("c", "-3");
int i = getPropertyTraditional(props, "b");
int j = getPropertyOptional(props, "b");
log.info("===> i : {}", i);
log.info("===> j : {}", j);
}
/**
* 从properties中读取指定key的值, 且数值必须大于0(传统实现)
*/
public static int getPropertyTraditional(Properties properties, String key) {
String val = properties.getProperty(key);
if (val != null) {
try {
int i = Integer.parseInt(val);
if (i > 0) {
return i;
}
} catch (NumberFormatException e) {
// ignore...
}
}
return -1;
}
/**
* 从properties中读取指定key的值, 且数值必须大于0(Optional实现)
*/
public static int getPropertyOptional(Properties properties, String key) {
return Optional.ofNullable(properties.getProperty(key)).flatMap(OptionalSample::stringToInt).filter(e -> e > 0).orElse(-1);
}
private static Optional<Integer> stringToInt(String s) {
try {
return Optional.of(Integer.parseInt(s));
} catch (NumberFormatException e) {
return Optional.empty();
}
}
}
小结
- 你可以使用静态工厂方法Optional.empty、Optional.of以及Optional.ofNullable创建Optional对象。
- Optional类支持多种方法,比如map、flatMap、filter,它们在概念上与Stream类中对应的方法十分相似。
- 使用Optional能帮助你设计更好的API,用户只需要阅读方法签名,就能了解该方法是否接受一个Optional类型的值。