一、前言
Optional在Java 8中引入,目的是解决 NullPointerExceptions的问题,Optional 是一个可以为null的容器对象。它可以保存类型T的值,或者仅仅保存null。如果值存在,isPresent()方法会返回true,调用get()方法会返回该对象。
在Java 8之前,程序员将返回null而不是Optional,这种方法有一些缺点。一种是没有明确的方法来表示null可能是一个特殊值。
相比之下,在API中返回Optional是明确的声明,其中可能没有值。如果我们要确保不会出现空指针异常,则需要对每个引用进行显式的空检查,如下所示,我们之前的代码都这样写的:
MiniProgram miniProgram = accountProperties.getMiniProgram();
if(Objects.nonNull(miniProgram)){
WxMpTemplateMessage wxMpTemplateMessage = miniProgram.getWxMpTemplateMessage();
if(Objects.nonNull(wxMpTemplateMessage)){
....
}
}
Optional 的好处在于可以简化平日里一系列判断 null 的操作,使得用起来的时候看着不需要判断 null,纵享丝滑,表现出来好像用 Optional 就不需要关心空指针的情况。Optional 其实就是一个壳,里面放着原先的值,至于这个值是不是 null 另说,反正拿到的这个壳肯定不是 null。

使用了optional语法简化如下:
String templateId= Optional.ofNullable(accountProperties.getMiniProgram())
.map(MiniProgram::getWxMpTemplateMessage)
.map(WxMpTemplateMessage::getTemplateId).orElse("");
可以看到,如果用了 Optional,代码里不需要判空的操作,即使 miniProgram 、wxMpTemplateMessage为空的话,也不会产生空指针错误,这就是 Optional 带来的好处!
二、Optional的特性
public final class Optional<T> {
//Null指针的封装
private static final java.util.Optional<?> EMPTY = new java.util.Optional<>();
//内部包含的值对象
private final T value;
private Optional() ;
//返回EMPTY对象
public static<T> java.util.Optional<T> empty() ;
//构造函数,但是value为null,会报NPE
private Optional(T value);
//静态工厂方法,但是value为null,会报NPE
public static <T> java.util.Optional<T> of(T value);
//静态工厂方法,value可以为null
public static <T> java.util.Optional<T> ofNullable(T value) ;
//获取value,但是value为null,会报NoSuchElementException
public T get() ;
//返回value是否为null
public boolean isPresent();
//如果value不为null,则执行consumer式的函数,为null不做事
public void ifPresent(Consumer<? super T> consumer) ;
//过滤,如果value不为null,则根据条件过滤,为null不做事
public java.util.Optional<T> filter(Predicate<? super T> predicate) ;
//转换,在其外面封装Optional,如果value不为null,则map转换,为null不做事
public<U> java.util.Optional<U> map(Function<? super T, ? extends U> mapper);
//转换,如果value不为null,则map转换,为null不做事
public<U> java.util.Optional<U> flatMap(Function<? super T, java.util.Optional<U>> mapper) ;
//value为null时,默认提供other值
public T orElse(T other);
//value为null时,默认提供other值
public T orElseGet(Supplier<? extends T> other);
//value为null时,默认提供other值
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) ;
}
Optional 对象可以通过以下方法构建:
2.1 empty( ):
返回一个空的 Optional 对象。
private static final Optional<?> EMPTY = new Optional<>();
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
返回一个空的Optional实例,如果对象为空,请避免与Option.empty()返回的实例的{==}比较。因为不能保证它是一个单例。
// Creating an empty optional
Optional<Object> empty = Optional.empty();
2.2 of( ):
返回一个包含给定值的 Optional 对象,如果给定值为 null,则触发异常NullPointerException。
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
静态方法需要一个非null参数;否则,将引发空指针异常。因此,如果我们不知道参数是否为null,那就是我们使用 ofNullable的时候,下面将对此进行介绍。
String name = "java";
Optional<String> opt = Optional.of(name);
2.3 ofNullable( ):
返回一个描述给定值的 Optional 对象,如果不为 null,则返回一个包含该值的 Optional 对象,否则返回一个空的 Optional 对象。
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
Optional<String> optional = Optional.ofNullable(name());
private String name(){
String name = "Java";
return (name.length() > 5) ? name : null;}
Optional 提供了以下主要方法:
2.4 isPresent():
检查 Optional 对象是否包含值,如果包含,则返回 true,否则返回 false。如果所包含的对象不为null,则返回true,反之返回false。
public boolean isPresent() { return value != null; }
通常在对对象执行任何其他操作之前,先在Optional上调用此方法。
Optional<String> optional = Optional.of("java");
if (optional.isPresent()) {
//Do something, normally a get
}
2.5 get():
获取 Optional 对象中的值(如果存在)。如果 Optional 对象为空,则抛出 NoSuchElementException 异常。这就需要 orElse() 方法来配合使用。
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
public static void main(String[] args) {
Optional<String> optional1 = Optional.of("java");
if (optional1.isPresent()){
String value = optional1.get();
System.out.println(value);
}
}
2.6 orElse( ):
返回 Optional 对象中的值(如果存在),否则返回一个默认值。
public T orElse(T other) {
return value != null ? value : other;
}
该 orElse() 方法用于检索包装在Optional实例内的值,它采用一个充当默认值的参数,该 orElse() 方法返回包装的值(如果存在)及其参数,反之返回传参的值。
public static void main(String[] args) {
//orElse
String nullName = null;
String name = Optional.ofNullable(nullName).orElse("default_name");
System.out.println(name);
}
2.7 orElseGet( ):
返回 Optional 对象中的值(如果存在),否则获取一个可计算的值。
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
该orElseGet() 方法类似于 orElse()。但是,如果没有Optional值,则不采用返回值,而是采用供应商功能接口,该接口将被调用并返回调用的值。
//orElseGet
String name = Optional.ofNullable(nullName).orElseGet(() -> "john");
orElseGet 和 orElse 说明:
Optional 里有 orElseGet 和 orElse 这两个看起来挺相似的方法,都是处理当值为 null 时的兜底逻辑。可能你也在一些文章上看到说用 orElseGet 不要用 orElse ,因为在 Optional 有值时候 orElse 仍然会调用方法,所以后者性能比较差。其实从上面分析我们知道不论 Optional 是否有值,orElse 和 orElseGet 都会被执行,所以是怎么回事呢?
看下这个代码:上面的的代码只有orElse()方法输出了字符,orElseGet ()方法并没有输出,难道结论有误。orElse 确实性能会差?
我们来看下源码。可以看到两者的入参不同,一个就是普通参数,一个是 Supplier。我们已经得知不论
Optional.ofNullable
返回的是否是空 Optional,下面的逻辑还是会执行,所以 orElse 和 orElseGet 这两个方法无论如何都会执行。
因此 orElse(getName1()) 会被执行,在参数入栈之前,执行了 getName1 方法得到结果,然后入栈,而 orElseGet 的参数是 Supplier,所以直接入栈,然后在调用 other.get 的时候,getName 方法才会被触发执行,这就是两者的区别之处。
所以才会造成上面表现出的性能问题,因此不是 BUG,也不是有些文章说的 Optional 有值 orElse 也会被执行而 orElseGet 不会执行这样不准确的说法,相信现在你的心里很有数了。
2.8 orElseThrow( ):
返回 Optional 对象中的值(如果存在),否则抛出一个异常。
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
这里,如果 user 值为 null,会抛出 IllegalArgumentException。
User result = Optional.ofNullable(user).orElseThrow( () -> new IllegalArgumentException());
这个方法让我们有更丰富的语义,可以决定抛出什么样的异常,而不总是抛出 NullPointerException。
2.9 ifPresent()
如果存在值,则使用该值调用指定的使用者;否则,什么都不做。
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
如果您不熟悉Java 8,那么您可能会想知道:什么是消费者?简单来说,消费者是一种接受参数且不返回任何内容的方法。当使用 ifPresent时,这个方法就是一石二鸟。我们可以执行值存在性检查并使用一种方法执行预期的操作,如下所示:
public static void main(String[] args) {
Optional<String> optional = Optional.of("java");
optional.ifPresent(s -> System.out.println(s.length()));
}
3.0 filter()过滤值:
除了转换值之外,Optional 类也提供了按条件”过滤”值的方法。
filter() 接受一个 Predicate 参数,返回测试结果为 true 的值。如果测试结果为 false,会返回一个空的 Optional。
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
下面来看一个根据基本的账号密码登录验证来决定接受或拒绝 User(用户) 的示例:
public static void main(String[] args) {
User user = new User("anna@gmail.com", "1234", Lists.newArrayList());
Optional<User> result = Optional.of(user)
.filter(u -> u.getPassword() != null && u.getUsername().contains("@"));
assertTrue(result.isPresent());
System.out.println(result.get());
}
ps:如果通过过滤器测试,result 对象会包含非空值。
3.1 map()转换值:
map() 对值应用(调用)作为参数的函数,然后将返回的值包装在 Optional 中。这就使对返回值进行链试调用的操作成为可能!
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value));
}
}
public static void main(String[] args) {
User user = new User("anna@gmail.com", "1234", Lists.newArrayList());
String result = Optional.of(user)
.map(User::getUsername).orElse("account");
System.out.println(result);
}
3.2 flatMap():
Optional.flatMap()
是Java 8中引入的一个方法,用于处理Optional对象。这个方法接受一个函数作为参数,该函数应用于Optional对象中的值(如果存在),并返回一个新的Optional对象。
Optional.flatMap的作用是将一个Optional类型的对象映射为一个新的Optional类型的对象,可以同时去掉原对象中的空值。
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));
}
}
例如,使用它可以将一个Optional类型的对象中的Optional类型的对象转换为另一个Optional类型的对象,避免空指针异常!
public static void main(String[] args) {
User user = new User("anna@gmail.com", "1234", Lists.newArrayList());
String result = Optional.of(user)
.flatMap(s->Optional.of("aa"))
.orElse("account");
System.out.println(result);
}
三、总结
Optional 是一个可以为null的容器对象,主要用于处理可能为null的情况,以避免出现空指针异常。Optional 类的使用场景非常广泛,例如:
-
方法返回值:当一个方法的返回值可能为null时,我们可以将方法的返回值类型改为Optional,这样就可以明确告诉调用者该方法可能返回一个空值。调用者在使用该方法时,就必须显式地处理Optional对象,从而避免空指针异常的发生。 -
集合操作:当我们对一个集合进行操作时,有时会遇到集合为空的情况。使用Optional可以避免这种情况的发生。我们可以将集合对象包装成Optional对象,这样就可以在集合为空时返回一个空的Optional对象,而不是null。调用者在使用该集合时,就必须显式地处理Optional对象,从而避免空指针异常的发生。
总的来说,Optional 是一个非常有用的工具,它可以为我们提供更优雅、更安全的代码实现。
原文始发于微信公众号(明月予我):Java8新特性之Optional正确理解和用法
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/272780.html