避免用null来表示变量的缺失!
带来的问题:
Null是错误之源。
NullPointerException是目前Java程序开发中最典型的异常。
它会使你的代码膨胀。
它让你的代码充斥着深度嵌套的null检查,代码的可读性糟糕透顶。
它自身是毫无意义的。
null自身没有任何的语义,尤其是,它代表的是在静态类型语言中以一种错误的方式对缺失变量值的建模。
它破坏了Java的哲学。
Java一直试图避免让程序员意识到指针的存在,唯一的例外是: null指针。
它在Java的类型系统上开了个口子。
null并不属于任何类型,这意味着它可以被赋值给任意引用类型的变量。这会导致问题,
原因是当这个变量被传递到系统中的另一个部分后,你将无法获知这个null变量最初的
赋值到底是什么类型。
java.util.Optional<T>
在对象模型中使用Optional,无法序列化
Optional的设计初衷仅仅是要支持能返回Optional对象的语法
由于Optional类设计时就没特别考虑将其作为类的字段使用,所以它也并未实现Serializable接口。
可以将Optional看成最多包含一个元素的Stream对象
如何创建Optional对象:
- 声明一个空的Optional
静态工厂方法Optional.empty
Optional<Car> optCar = Optional.empty();
- 依据一个非空值创建Optional
静态工厂方法Optional.of
Optional<Car> optCar = Optional.of(car); // 如果car是一个null,这段代码会立即抛出一个NullPointerException
- 可接受null的Optional
静态工厂方法Optional.ofNullable,可以创建一个允许null值的Optional对象
Optional<Car> optCar = Optional.ofNullable(car);
使用 map 从 Optional 对象中提取和转换值
String name = null;
if(insurance != null){
name = insurance.getName();
}
# 改进
Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);
使用 flatMap 链接 Optional 对象
public String getCarInsuranceName(Person person) {
return person.getCar().getInsurance().getName();
}
# 改进
public String getCarInsuranceName(Optional<Person> person) {
return person.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse("Unknown");
}
Optional类提供了多种方法读取Optional实例中的变量值:
get()是这些方法中最简单但又最不安全的方法。
如果变量存在,它直接返回封装的变量值,否则就抛出一个NoSuchElementException异常。
所以,除非非常确定Optional变量一定包含值,否则使用这个方法是个相当糟糕的主意。
此外,这种方式即便相对于嵌套式的null检查,也并未体现出多大的改进。
orElse(T other),允许在Optional对象不包含值时提供一个默认值。
orElseGet(Supplier<? extends T> other)是orElse方法的延迟调用版,
Supplier方法只有在Optional对象不含值时才执行调用。
如果创建默认值是件耗时费力的工作,你应该考虑采用这种方式(借此提升程序的性能),
或者你需要非常确定某个方法仅在Optional为空时才进行调用,
也可以考虑该方式(这种情况有严格的限制条件)。
orElseThrow(Supplier<? extends X> exceptionSupplier)
和get方法非常类似,
它们遭遇Optional对象为空时都会抛出一个异常,
但是使用orElseThrow你可以定制希望抛出的异常类型。
ifPresent(Consumer<? super T>),
让你能在变量值存在时执行一个作为参数传入的方法,否则就不进行任何操作。
Optional类和Stream接口的相似之处,远不止map和flatMap这两个方法。
还有第三个方法filter,它的行为在两种类型之间也极其相似
isPresent方法,如果Optional对象包含值,该方法就返回true
使用 filter 剔除特定的值
Insurance insurance = ...;
if(insurance != null && "CambridgeInsurance".equals(insurance.getName())){
System.out.println("ok");
# 改进
Optional<Insurance> optInsurance = ...;
optInsurance.filter(insurance ->
"CambridgeInsurance".equals(insurance.getName()))
.ifPresent(x -> System.out.println("ok"));
public String getCarInsuranceName(Optional<Person> person, int minAge) {
return person.filter(p -> p.getAge() >= minAge)
.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse("Unknown");
}
Optional类的方法:
empty
返回一个空的 Optional 实例
filter
如果值存在并且满足提供的谓词,就返回包含该值的 Optional 对象;否则返回一个空的Optional对象
flatMap
如果值存在,就对该值执行提供的 mapping 函数调用,返回一个 Optional 类型的值,否则就返回一个空的 Optional 对象
get
如果该值存在,将该值用 Optional 封装返回,否则抛出一个 NoSuchElementException 异常
ifPresent
如果值存在,就执行使用该值的方法调用,否则什么也不做
isPresent
如果值存在就返回 true,否则返回 false
map
如果值存在,就对该值执行提供的 mapping 函数调用
of
将指定值用 Optional 封装之后返回,如果该值为 null,则抛出一个 NullPointerException异常
ofNullable
将指定值用 Optional 封装之后返回,如果该值为 null,则返回一个空的 Optional 对象
orElse
如果有值则将其返回,否则返回一个默认值
orElseGet
如果有值则将其返回,否则返回一个由指定的 Supplier 接口生成的值
orElseThrow
如果有值则将其返回,否则抛出一个由指定的 Supplier 接口生成的异常
实战:
public static Optional<Integer> stringToInt(String s) {
try {
return Optional.of(Integer.parseInt(s));
} catch (NumberFormatException e) {
return Optional.empty();
}
}
Optional<Object> value = Optional.ofNullable(map.get("key"));
public int readDuration(Properties props, String name) {
return Optional.ofNullable(props.getProperty(name))
.flatMap(OptionalUtility::stringToInt)
.filter(i -> i > 0)
.orElse(0);
}
总结:
null引用在历史上被引入到程序设计语言中,目的是为了表示变量值的缺失。
Java 8中引入了一个新的类java.util.Optional<T>,对存在或缺失的变量值进行建模。
可以使用静态工厂方法Optional.empty、 Optional.of以及Optional.ofNullable创建Optional对象。
Optional类支持多种方法,比如map、 flatMap、 filter,它们在概念上与Stream类中对应的方法十分相似。
使用Optional会迫使你更积极地解引用Optional对象,以应对变量值缺失的问题,最终,你能更有效地防止代码中出现不期而至的空指针异常。
使用Optional能帮助你设计更好的API,用户只需要阅读方法签名,就能了解该方法是否接受一个Optional类型的值。
网友评论