NullPointerException,大家应该都见过。这是Tony Hoare在设计ALGOL W语言时提出的null引用的想法,他的设计初衷是想通过编译器的自动检测机制,确保所有使用引用的地方都是绝对安全的。很多年后,他对自己曾经做过的这个决定而后悔不已,把它称为“我价值百万的重大失误”。它带来的后果就是---我们想判断一个对象中的某个字段进行检查,结果发现我们查看的不是一个对象,而是一个空指针,他会立即抛出NullPointerException异常。
看下面这个例子:
public class Person {
private Car car;
public Car getCar() {
return car;
}
}
public class Car {
private Insurance insurance;
public Insurance getInsurance() {
return insurance;
}
}
public class Insurance {
private String name;
public String getName() {
return name;
}
}
下面这个方法有什么问题呢?
public String getCarInsuranceName(Person p){
return p.getCar().getInsurance().getName();
}
这是一个获取保险公司名字的方法,但是在库里可能很多人没有车,所以会返回null引用。更没有车险,所以直接返回一个NullPointerException。
为了避免这种情况,我们一般会在需要的地方添加null的检查,并且添加的方式往往不同。
避免NullPointerException第一次尝试:
public String getCarInsuranceName(Person p){
if(p != null){
Car car = p.getCar();
if(car != null){
Insurance insurance = car.getInsurance();
if(insurance != null){
return insurance.getName();
}
}
}
return "Unknown";
}
这个方法每次引用一个变量时,都会做一次null检查,如果任何一个返回值为null,则会返回Unknown。因为知道公司都必须有名字,所以最后一个保险公司的名字没有进行判断。这种方式不具备扩展性,同时还牺牲了代码的可读性。每一次都要嵌套一个if来进行检查。
避免NullPointerException第二次尝试:
public String getCarInsuranceName(Person p) {
if (p == null) return "Unknown";
Car car = p.getCar();
if (car == null) return "Unknown";
Insurance insurance = car.getInsurance();
if (insurance == null) return "Unknown";
return insurance.getName();
}
第二种方式,避免了深层if语句块,采用了每次遇到null都直接返回Unknown字符串的方式。然后这个方案也并非理想,现在这个方法有了四个截然不同的退出点,使代码的维护更艰难。发生null时的默认值,在三个不同的地方出现,不知道具体是哪个返回null。
Optional类
Java 8中引入了一个新的类java.util.Optional<T>。这是一个封装Optional值的类。当变量存在时,Optional类知识对类简单封装,变量不存在时,缺失的值被建模成一个空的Optional对象,由方法Optional.empty()返回。该方法是一个静态工厂方法,返回Optional类的特定单一实例。
null和Optional.empty()从语义上,可以当做是一回事。实际上它们之间的差别非常大:如果你尝试访问一个null,一定会触发null引用。而Optional.empty()可以在任何地方访问。
public class Person {
private Optional<Car> car;
public Optional<Car> getCar() {
return car;
}
}
public class Car {
private Optional<Insurance> insurance;
public Optional<Insurance> getInsurance() {
return insurance;
}
}
公司的名字我们没有使用Optional<String> 而是保持了原类型String,那么它就必须设置一个值。
创建Optional对象
1.声明一个空的Optional
Optional<Car> car = Optional.empty();
2.依据一个非空值创建Optional
Car car = new Car();
Optional<Car> optCar = Optional.of(car);
如果car是null,则直接会报错null引用,而不是等到你访问时。
3.可接受null的Optional,这种方式与of不同,编译器运行时不会报错。
Car car = null;
Optional<Car> optCar = Optional.ofNullable(car);
使用map从Optional对象中提取和转换值