禁止空指针,该怎么避免崩溃的空指针

105 阅读2分钟

降低空指针的负面影响的最重要的办法,就是不要产生空指针。没有空指针的代码,代码更简洁,风险也更小。

在很多场景下,我们都可以使用空值来替代空指针,比如,空的字符串、空的集合。

不过,不是在所有的情况下我们都能够避免空指针的。如果空指针不能避免,降低空指针的负面影响的另外一个办法,就是在使用空指针的时候,执行强制性的检查。所谓强制性的检查,对于编程语言来说,指的是我们通常能够依赖的是编译器的能力,以及新的接口设计思路。

设计 Optional 的目的,是希望开发者能够先调用它的 Optional.isPresent 方法,然后再调用 Optional.get 方法获得目标对象。 按照设计者的预期,这个 Optional 类的使用应该像下面的代码这样。

private static boolean hasMiddleName(
        FullName fullName, String middleName) {
    if (fullName.middleName().isPresent()) {
        return fullName.middleName().get().equals(middleName);
    }

    return middleName == null;
}

Optional 带来了不必要的复杂性,然而它并没有简化开发者的工作,也没有解决掉空指针的问题。

我们希望返回值的检查是强制性的。如果不检查,就没有办法得到返回值指代的真实对象。实现的思路,就是使用封闭类和模式匹配。

首先呢,我们定义一个指代返回值的封闭类 Returned。为什么使用封闭类呢,因为封闭类的子类可查可数。可查可数,也就意味着我们可以有简单的模式匹配。

public sealed interface Returned<T> {
    Returned.Undefined UNDEFINED = new Undefined();

    record ReturnValue<T>(T returnValue) implements Returned {
    }

    record Undefined() implements Returned {
    }
}

然后呢,我们就可以使用 Returned 来表示返回值了。

public final class FullName {
    // snipped
    public Returned<String> middleName() {
        if (middleName == null) {
            return Returned.UNDEFINED;
        }

        return new Returned.ReturnValue<>(middleName);
    }
    // snipped
}

最后,我们来看看 Returned 是怎么使用的。

private static boolean hasMiddleName(FullName fullName, String middleName) {
    return switch (fullName.middleName()) {
        case Returned.Undefined undefined -> false;
        case Returned.ReturnValue rv -> {
            String returnedMiddleName = (String)rv.returnValue();
            yield returnedMiddleName.equals(middleName);
        }
    };
}

此文章为9月Day16学习笔记,内容来源于极客时间《深入剖析 Java 新特性》