转载

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

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

2. Java 中的 null

翻译自 Oracle Java 文档

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()返回一个值为 nullOptional 实例

Optional<Object> empty = Optional.empty();
复制代码

of(T)返回一个值不为 nullOptional 实例

Optional<String> nonNull = Optional.of("Felordcn");
复制代码

ofNullable()返回一个值可能为 nullOptional 实例

// 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()将 OptionalStream 打通

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

5. Optional 的使用误区

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

6. 总结

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

关注公众号:Felordcn获取更多资讯

个人博客:https://felord.cn

Java 是如何优雅地处理NPE问题的
原文  https://juejin.im/post/5de50e355188253232282e79
正文到此结束
Loading...