ITPub博客

首页 > IT职业 > > Java 是如何优雅地处理NPE问题的

Java 是如何优雅地处理NPE问题的

作者:码农小胖哥 时间:2020-05-28 15:52:57 0 删除 编辑

1. 前言

对于 Java 开发者来说,null 是一个令人头疼的类型,一不小心就会发生 NPE (空指针) 问题。也是 Java 语言为人诟病的一个重要原因之一。在我们消除可恶的 NPE 问题之前我们要回顾一下 Java 中 null 的概念。

2. Java 中的 null

翻译自 Oracle Java 文档[1]

Java 语言中有两种类型,一种是 基本类型 ,另一种是 引用类型。还有一种没有名字的特殊类型,即表达式 null 。由于 null 类型没有名称,所以不可能声明为 null 类型的变量或者转换为 null 类型。null 引用是null 类型表达式唯一可能的值。null 引用可以转换为任意引用类型。事实上,程序员可以忽略null类型,可以认为null仅仅是一个可以成为任何引用类型的特殊符号。

从上面的描述我们可以了解到,其实 null 仅仅是一个关键字标识量,既不是一种类型也不算对象,无法直接声明 null 和被转换为 null,仅仅只能被引用,null 可以转换为任何引用类型。当一个 Java 引用类型对象被引用为 null 时代表当前对象不引用对象,并没有为其分配内存。 这也是我们在没有引用的对象上调用方法出现空指针的根本原因。大多数情况下 Java 开发者使用 null 是为了表示某种不存在的意思。

3. NPE 问题的解决

很多时候我们对数据是否存在有自己的期望,但是这种期望并不能直接被我们掌控,一个返回值为 null 所表达的意思并不明确过于模糊,往往通过是否判断为 null 来规避空指针问题。于是 Google 工程师在他们的 Guava 工具类库中设计了 Optional<T> 来解决 null 不可控的问题。让你在不得不使用 null 的时候,可以更加简便明确的使用 null 并帮助你避免直接使用 null 带来的问题。Java 8 将此设计吸收。我们可以直接使用 Java 提供的 Optional 来解决空指针问题。接下来我们来研究一下 Java 8 中的 Optional 。

4. Java 8 中的 Optional

Java 8 中的 Optional 是一个可选值的包装类。它的意义不仅仅帮我们简化了 NPE 问题的处理,同时也是 Java 函数式编程的一个重要辅助。我们接下来将对其 API 进行讲解以帮助你在实际开发中使用他们。

4.1 Optional 声明

Optional 只能通过静态方法来声明。它提供了三个静态方法:

empty() 返回一个值为 null 的 Optional 实例

  Optional<Object> empty = Optional.empty();

of(T) 返回一个值不为 null 的 Optional 实例

 Optional<String> nonNull = Optional.of("Felordcn");

ofNullable() 返回一个值可能为 null 的 Optional 实例

 // value 值来自其它不确定的来源
String value = SomeApi.source();
// 可能为 null
Optional<String> nullable = Optional.ofNullable(value);
// 也可能不为 null
Optional<String> hasValue = Optional.ofNullable(value);

4.2 其它方法

isPresent() 如果值存在则返回 true,否则返回 false 。如果 Optional 值不确定,可使用该方法进行安全校验

 Optional<String> nonNull = Optional.of("Felordcn");
// true
boolean present =nonNull.isPresent();

get() 获取 Optional 中的值,如果为空会抛出 NoSuchElementException 异常

 Optional<String> nonNull = Optional.of("Felordcn");
// Felordcn
String str = nonNull.get();

ifPresent(Consumer) 如果值存在则该值被消费函数 Consumer 消费 , 否则不做任何事情。isPresent() 加强版


// 非空打印出字符串
nullable.ifPresent(System.out::println);

//等同于
if (nullable.isPresent()) {
System.out.println(nonNull);
}

filter(Predicate) 如果值满足断言函数 Predicate 则返回该 Optional,否则返回 Optional.empty()

 Optional<String> nonNull = Optional.of("Felordcn");
Optional<String> felord = nonNull.filter(s -> s.startsWith("Felord"));
// str = "Felordcn"
String str = felord.get();

map(Function) 获取元素某个属性的 Optional 。如果该属性为 null 返回 Optional.empty() ,否则返回对应值的 Optional

 Optional<User> userOpt = Optional.ofNullable(user);
// username 为空 则为 空 Optional
Optional<String> usernameOpt = userOpt.map(User::getUsername);

flatMap(Function) 有时候我们会返回 Optional<Optional<T>> 非常不便于处理,我们需要将元素展开,可使用该方法处理,参考 Stream Api 中的相关方法

orElse(other) 如果 Optional 的值存在,返回 Optional, 否则指定一个 Optional

orElseGet(Supplier) 如果 Optional 的值存在,返回 Optional, 否则指定一个执行 Supplier 函数来获取值

orElseThrow(Supplier<? extends Throwable>) 如果 Optional 的值存在,返回 Optional, 否则抛出一个指定 Supplier 函数提供的异常

4.3 Java 9 中的新 API

or(Supplier) orElseGet 的改进类型。不单单返回具体的值,而可以函数式的返回 Optional

stream() 将 Optional 和 Stream 打通

ifPresentOrElse(Consumer) ifPresent 方法提供了有值后的消费逻辑而没有值的逻辑没有提供入口。新方法 ifPresentOrElse 弥补了这一缺陷

5. Optional 的使用误区

Optional 很香但是也不能滥用。一个危险的举动就是将 Optional 作为入参传递给方法。因为入参是不可控的,你无法保证入参中的 Optional 是否为 null。这恰恰违背了 Optional 的本意。所以尽量在表达式中使用 Optional 或者在返回值中使用,而不是在方法的参数中使用 Optional 。

6. 总结

今天对 Optional 进行讲解。从 Optional 的设计本意到其常用的方法。我们也对 Optional 在 Java 9 中的新 API 进行了介绍。另外 Optional 也不是万能的,合理的使用才能发挥其优势。希望今天的文章对你有用。

参考资料

[1]

Oracle Java 文档: https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.1



来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69975884/viewspace-3122821/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2020-06-23

  • 博文量
    377
  • 访问量
    8061