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
也不是万能的,合理的使用才能发挥其优势。希望今天的文章对你有用。
java培训班:http://www.baizhiedu.com/java2019
注释:本文内容来自公众号码农小胖哥