10.1 如何为缺失的值建模

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 person) {
  	return person.getCar().getInsurance().getName();
}

上面这段代码存在的问题:

  1. person为null,导致NPE;
  2. person.getCar()为null,导致NPE;
  3. person.getCar().getInsurance()为null,导致NPE;
  4. person.getCar().getInsurance().getName(),业务上是否可以返回null;

10.1.1 采用防御式检查减少NullPointerException

通过每层嵌套判断null,避免NPE发生

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";
}

难以扩展,且嵌套太深,代码可读性差

通过每次检查null,直接退出

private String DEFAULT_CAR_INSURANCE_NAME = "Unknown";
public String getCarInsuranceName(Person person) {
    if (person == null) {
        return DEFAULT_CAR_INSURANCE_NAME;
    }

    Car car = person.getCar();
    if (car == null) {
        return DEFAULT_CAR_INSURANCE_NAME;
    }

    Insurance insurance = car.getInsurance();
    if (insurance == null) {
        return DEFAULT_CAR_INSURANCE_NAME;
    }

    return insurance.getName();
}

这种是我们业务上常用的写法,极易遗漏null的检查。

10.1.2 null带来的种种问题

在Java程序开发中使用null,会带来理论和实际操作上的一些问题。

  1. 它是错误之源:NullPointerException是目前Java程序开发中最典型的异常;
  2. 它会使你的代码膨胀:业务代码中充斥着各种null检查,代码的可读性差;
  3. 它自身是毫无意义的:null自身没有任何的语义,尤其是,它代表的是在静态类型语言中以一种错误的方式对缺失变量值的建模
  4. 它破坏了Java的哲学:Java一直试图避免让程序员意识到指针的存在,而nullPointer暴露了这点;
  5. 它在Java的类型系统上开了个口子:null并不属于任何类型,可以被赋值给任意引用类型的变量。会导致当null类型变量被传递到系统中另一个部分后,无法获知这个null变量最初的赋值类型。

第5点暴露了java很多问题

  1. java类型系统,还包括了基本类型,不完全是面向对象;
  2. 泛型也有这个问题,被擦除后,不知道原有类型是什么;

10.1.3 其他语言中null的替代品

  1. 增加语法糖,在调用链中的变量遭遇null时,将null引用沿着调用链传递下去,最终返回null。

    Groovy通过引入安全导航操作符(Safe Navigation Operator,标记为?)可以安全访问可能为null的变量。

def carInsuranceName = person ?.car ?.insurance ?.name
  1. 对optional值的封装

    Haskell中包含了一个Maybe类型,可以是指定类型的值,也可以什么都不是

    Scala有类似的数据结构Option[T],既可以包含类型为T的变量,也可以不包含该变量

Java 8采取封装optional值,引入了新类java.util.Optional

考虑数据模型或者业务中是否应该返回一个null,尝试使用Optional代替null。

posted @ 2023-05-28 14:02  蒋智  阅读(21)  评论(0)    收藏  举报