[Java8]-传统的Null校验和引发的问题

190 阅读3分钟

1.0 如何为缺失的值建模

1.1 建立一个个拥有汽车及汽车保险的客户。

/**
 * <b>功能描述:保险</b><br> 
 * @author newzhong
 * @version 1.0.0
 * @since JDK 1.8
 */
@Data
public class Insurance {
    private String name;
}

@Data
public class Car {
    private Insurance insurance;
}
package java8.stream.第10章Optional;

import org.junit.Test;

public class PersonTest {

    @Test
    public void getCarInsuranceName() {
        Person person = new Person();
        String carInsuranceName = person.getCarInsuranceName(person);
    }
}

但是很多人没有车。所以调用getCar方法的结果会 NullPointerException。为了避免这种情况,就是返回一个null引用,表示该值的缺失,即用户没有车。而接下来,对getInsurance的调用会返回null引用的insurance,这会导致运行时出现

2.0 采用防御式检查减少 NullPointerException

2.1 避免 null-安全的第一种尝试:深层质疑

NullPointerException可以添加null的检查

/**
     * <b>功能描述:采用防御式检查减少 NullPointerException</b>
     * @author    zhong
     * @since     JDK 1.8
     *
     * @return String
     * @param person
     */
    public String getCarInsuranceNameFirst(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检查,如果引用链上的任何一个遍历的解变量值为null,它就返回一个值为“Unknown”的字符串。唯一的例外是保险公司的名字,你不需要对它进行检查,原因很简单,因为任何一家公司必定有个名字。但是它不断重复着一种模式:每次你不确定一个变量是否为null时,都需要添加嵌套的if块,增加代码缩进的层数。这种方式不具备扩展性,可读性差。

2.2 null-安全:过多的退出语句

  1. 检查null的代码
public String getCarInsuranceNameSafe(Person person){
        // 逆向思维,一般都是判断不为空的情况
        if (person == null){
            // 每个null都增加新的退出点
            return "unknown";
        }
        Car car = person.getCar();
        if (car == null){
            return "unknown";
        }
        Insurance insurance = car.getInsurance();
        if (insurance == null){
            return "unknown";
        }
        return insurance.getName();
    }
  1. 测试结果
    @Test
    public void getCarInsuranceNameSafe(){
        Person person = new Person();
        String carInsuranceNameSafe = person.getCarInsuranceNameSafe(person);
        assertEquals("unknown",carInsuranceNameSafe);
    }

虽然避免深层递归的if语句块,但是:每次你遭遇null 变量,都返回一个字符串常量Unknown。然而,在这个方法有了四个截然不同的退出点,如果流程多,退出点也随之变多,使得代码的维护异常艰难。这种流程是极易出错的;如果你忘记检查了那个可能为null的属性将会空指针异常。

3.0 null引发的问题

  • 1.错误之源 NullPointerException是目前Java程序开发中最典型的异常
  • 2.代码膨胀 存在深度嵌套的null检查,代码的可读性糟糕透顶。
  • 3.null是无意义的 它代表的是在静态类型语言中以一种错误的方式对缺失变量值的建模。
  • 4.破坏的Java的哲学 Java一直试图避免让程序员意识到指针的存在,唯一的例外是:null指针。
  • 5.在Java的类型系统上开了个口子。 null并不属于任何类型,意味着它可以被赋值给任意引用类型的变量。这会导致问题, 当这个变量被传递到系统中的另一个部分后,你将无法获知这个null变量最初的赋值到底类型是什么。