乐趣区

关于程序员:成功案例正确的倍投6种方法

`「胜利案例」正确的倍投 6 种办法陈北叩【753~7227——码:9588~9990】

Java Optional 使用指南

2021-01-29 09:46  申城异乡人  浏览(146)  评论(1)  编辑  珍藏)

提到 NullPointerException(简称 NPE)异样,置信每个 Java 开发人员都不生疏,从接触编程的第 1 天起,它就和咱们如影随形,最近解决的线上 bug 中,有不少都是对象没判空导致的 NullPointerException 异样。

  1. 简略回顾

引起 NullPointerException 异样的中央有很多,比方调用 String 的 trim()办法,比方对 BigDecimal 进行计算时,比方将包装类型转化为根本类型时,这里简略回顾下。

假如有个导入模版定义如下:

“java
package com.zwwhnly.springbootaction.model;

import lombok.AllArgsConstructor;
import lombok.Data;

/**

  • 导入模版

*/
@Data
@AllArgsConstructor
public class ImportTemplate {

/**
 * 模版 id
 */
private int templateId;

/**
 * 模版名称
 */
private String templateName;

/**
 * 模版下载 url
 */
private String url;

/**
 * 备注
 */
private String remark;

}
`

而后看下如下代码:

`java
public static void main(String[] args) {

ImportTemplate importTemplate = getImportTemplateById(1);
System.out.println(importTemplate.getUrl());

}

public static ImportTemplate getImportTemplateById(int id) {

return new ImportTemplate(1, "销售订单 - 普通商品导入模版", "o_w-140e3c1f41c94f238196539558e25bf7", null);

}
`

失常状况下,这段代码必定是没有问题的,但当 getImportTemplateById 办法返回 null 时,这段代码就会抛出 NullPointerException 异样,如下所示:

`
public static ImportTemplate getImportTemplateById(int id) {

return null;

}
`

为了程序能失常运行,就要判断 importTemplate 是否为 null,所以代码就批改为了:

`java
public static void main(String[] args) {

ImportTemplate importTemplate = getImportTemplateById(1);
if (importTemplate != null) {System.out.println(importTemplate.getUrl());
}

}
`

我的项目中相似的判空代码应该有很多,大家能够自行看下本人我的项目的代码。

  1. 应用 Optional

为了防止 NullPointerException 异样,JDK1.8 新增了 Optional 类来解决空指针异样,该类位于 java.util 包下,提供了一系列办法,

并且能够配合 Lambda 表达式一起应用,使代码看起来更加清晰,接下来咱们看下它的应用办法。

2.1 创立实例

创立 Optional 实例有以下 3 种形式,别离为:

  1. 调用 empty 办法

    `java
    Optional<ImportTemplate> optionalImportTemplate = Optional.empty();
    `

  2. 调用 of 办法

    `java
    ImportTemplate importTemplate = new ImportTemplate(1, “ 销售订单 - 普通商品导入模版 ”,

        "o_w-140e3c1f41c94f238196539558e25bf7", null);

    Optional<ImportTemplate> optionalImportTemplate = Optional.of(importTemplate);
    `

  3. 调用 ofNullable 办法(举荐)

    `java
    ImportTemplate importTemplate = new ImportTemplate(1, “ 销售订单 - 普通商品导入模版 ”,

        "o_w-140e3c1f41c94f238196539558e25bf7", null);

    Optional<ImportTemplate> optionalImportTemplate = Optional.ofNullable(importTemplate);
    `

值得注意的是,当参数为 null 时,调用 of 办法会抛 NullPointerException 异样,但调用 ofNullable 办法不会(更合乎应用场景),因而 举荐应用 ofNullable 办法

`java
ImportTemplate importTemplate = null;
Optional<ImportTemplate> optionalImportTemplate = Optional.of(importTemplate);
`

2.2 判断是否有值

能够调用 isPresent 办法来判断对象是否有值(不为 null),应用办法如下所示:

`java
ImportTemplate importTemplate = null;
Optional<ImportTemplate> optionalImportTemplate = Optional.ofNullable(importTemplate);

System.out.println(optionalImportTemplate.isPresent());
`

以上代码的输入后果为:

`java
ImportTemplate importTemplate = new ImportTemplate(1, “ 销售订单 - 普通商品导入模版 ”,

    "o_w-140e3c1f41c94f238196539558e25bf7", null);

Optional<ImportTemplate> optionalImportTemplate = Optional.ofNullable(importTemplate);

System.out.println(optionalImportTemplate.isPresent());
`

以上代码的输入后果为:

看下 isPresent 的源码,逻辑非常简单,就是判断了咱们传入的对象是否有值,即不为 null:

`java
/**

  • Return {@code true} if there is a value present, otherwise {@code false}.

*

  • @return {@code true} if there is a value present, otherwise {@code false}

*/
public boolean isPresent() {

return value != null;

}
`

2.3 获取值

能够调用 get 办法来获取对象的有值,应用办法如下所示:

`java
ImportTemplate importTemplate = new ImportTemplate(1, “ 销售订单 - 普通商品导入模版 ”,

    "o_w-140e3c1f41c94f238196539558e25bf7", null);

Optional<ImportTemplate> optionalImportTemplate = Optional.ofNullable(importTemplate);
System.out.println(optionalImportTemplate.get());
`

以上代码的输入后果为:

值得注意的是,当咱们传入的对象为 null 时,调用 get 办法会抛出 java.util.NoSuchElementException 异样,而不是返回 null。

`java
ImportTemplate importTemplate = null;
Optional<ImportTemplate> optionalImportTemplate = Optional.ofNullable(importTemplate);
System.out.println(optionalImportTemplate.get());
`

以上代码的输入后果为:

看下 get 办法的源码,就能够晓得起因:

`java
public T get() {

if (value == null) {throw new NoSuchElementException("No value present");
}
return value;

}
`

2.4 先用 isPresent,再用 get(不举荐)

而后咱们回顾下文初的代码:

`java
ImportTemplate importTemplate = getImportTemplateById(1);
if (importTemplate != null) {

System.out.println(importTemplate.getUrl());

}
`

可能很多同学会把代码优化为上面这样的写法:

`java
Optional<ImportTemplate> optionalImportTemplate = Optional.ofNullable(getImportTemplateById(1));
if (optionalImportTemplate.isPresent()) {

System.out.println(optionalImportTemplate.get().getUrl());

}
`

不举荐这么应用,因为判断的中央没缩小,而且还不如原来看起来清晰。

2.5 ifPresent(举荐)

那该怎么优化呢?答案就是应用 ifPresent 办法,该办法接管一个 Consumer 类型的参数,当值不为 null 时,就执行,当值为 null 时,就不执行,源码如下所示:

`java
public void ifPresent(Consumer<? super T> consumer) {

if (value != null)
    consumer.accept(value);

}
`

优化之后的代码如下所示:

`java
Optional<ImportTemplate> optionalImportTemplate = Optional.ofNullable(getImportTemplateById(1));
optionalImportTemplate.ifPresent(importTemplate -> System.out.println(importTemplate.getUrl()));
`

当然,也能够写更多的逻辑:

`java
Optional<ImportTemplate> optionalImportTemplate = Optional.ofNullable(getImportTemplateById(1));
optionalImportTemplate.ifPresent(importTemplate -> {

System.out.println(importTemplate.getTemplateId());
System.out.println(importTemplate.getTemplateName());
System.out.println(importTemplate.getUrl());
System.out.println(importTemplate.getRemark());

});
`

2.6 自定义默认值

Optional 类提供了以下 2 个办法来自定义默认值,用于当对象为 null 时,返回自定义的对象:

  1. orElse
  2. orElseGet

先来看下 orElse 办法的应用:

`java
public static void main(String[] args) {

ImportTemplate importTemplate = null;
ImportTemplate firstImportTemplate = Optional.ofNullable(importTemplate)
        .orElse(getDefaultTemplate());
System.out.println(firstImportTemplate);

importTemplate = new ImportTemplate(2, "销售订单 - 不定规格商品导入模版", "o_w-a7109db89f8d4508b4c6202889a1a2c1", null);

ImportTemplate secondImportTemplate = Optional.ofNullable(importTemplate)
        .orElse(getDefaultTemplate());
System.out.println(secondImportTemplate);

}

public static ImportTemplate getDefaultTemplate() {

System.out.println("getDefaultTemplate");
return new ImportTemplate(1, "销售订单 - 普通商品导入模版", "o_w-140e3c1f41c94f238196539558e25bf7", null);

}
`

输入后果:

再来看下 orElseGet 办法的应用:

`java
public static void main(String[] args) {

ImportTemplate importTemplate = null;
ImportTemplate firstImportTemplate = Optional.ofNullable(importTemplate)
        .orElseGet(() -> getDefaultTemplate());
System.out.println(firstImportTemplate);

importTemplate = new ImportTemplate(2, "销售订单 - 不定规格商品导入模版", "o_w-a7109db89f8d4508b4c6202889a1a2c1", null);

ImportTemplate secondImportTemplate = Optional.ofNullable(importTemplate)
        .orElseGet(() -> getDefaultTemplate());
System.out.println(secondImportTemplate);

}

public static ImportTemplate getDefaultTemplate() {

System.out.println("getDefaultTemplate");
return new ImportTemplate(1, "销售订单 - 普通商品导入模版", "o_w-140e3c1f41c94f238196539558e25bf7", null);

}
`

输入后果:

从输入后果看,2 个办法如同差不多,第 1 次调用都返回了默认模版,第 2 次调用都返回了传入的模版,但其实仔细观察,你会发现当应用

orElse 办法时,getDefaultTemplate 办法执行了 2 次,但调用 orElseGet 办法时,getDefaultTemplate 办法只执行了 2 次(只在第 1 次传入模版为 null 时执行了)。

为什么会这样呢?带着这个疑难,咱们看下这 2 个办法的源码,其中 orElse 办法的源码如下所示:

`java
public T orElse(T other) {

return value != null ? value : other;

}
`

能够看到,参数 other 是个对象,这个参数必定是要传的,但只有 value 为空时,才会用到(返回)这个对象。

orElseGet 办法的源码如下所示:

`java
public T orElseGet(Supplier<? extends T> other) {

return value != null ? value : other.get();

}
`

能够看到,参数 other 并不是间接传入对象,如果 value 为 null,才会执行传入的参数获取对象,如果不为 null,间接返回 value。

2.7 自定义异样

Optional 类提供了 orElseThrow 办法,用于当传入的对象为 null 时,抛出自定义的异样,应用办法如下所示:

`java
public static void main(String[] args) {

ImportTemplate importTemplate = new ImportTemplate(2, "销售订单 - 不定规格商品导入模版", "o_w-a7109db89f8d4508b4c6202889a1a2c1", null);
ImportTemplate firstImportTemplate = Optional.ofNullable(importTemplate)
        .orElseThrow(() -> new IndexOutOfBoundsException());
System.out.println(firstImportTemplate);

importTemplate = null;

ImportTemplate secondImportTemplate = Optional.ofNullable(importTemplate)
        .orElseThrow(() -> new IndexOutOfBoundsException());
System.out.println(secondImportTemplate);

}
`

输入后果:

2.8 过滤数据

Optional 类提供了 filter 办法来过滤数据,该办法接管一个 Predicate 参数,返回匹配条件的数据,如果不匹配条件,返回一个空的 Optional,应用办法如下所示:

`java
public static void main(String[] args) {

ImportTemplate importTemplate = getImportTemplateById(1);
Optional<ImportTemplate> filterById = Optional.ofNullable(importTemplate)
        .filter(f -> f.getTemplateId() == 1);
System.out.println(filterById.isPresent());

Optional<ImportTemplate> filterByName = Optional.ofNullable(importTemplate)
        .filter(f -> f.getTemplateName().contains("发货单"));
System.out.println(filterByName.isPresent());

}

public static ImportTemplate getImportTemplateById(int id) {

return new ImportTemplate(1, "销售订单 - 普通商品导入模版", "o_w-140e3c1f41c94f238196539558e25bf7", null);

}
`

输入后果:

2.9 转换值

Optional 类提供了以下 2 个办法来转换值:

  1. map
  2. flatMap

map 办法的应用办法如下所示:

`java
public static void main(String[] args) {

ImportTemplate importTemplate = getImportTemplateById(1);
Optional<String> optionalUrl = Optional.ofNullable(importTemplate)
        .map(f -> "url:" + f.getUrl());
System.out.println(optionalUrl.isPresent());
System.out.println(optionalUrl.get());

}

public static ImportTemplate getImportTemplateById(int id) {

return new ImportTemplate(1, "销售订单 - 普通商品导入模版", "o_w-140e3c1f41c94f238196539558e25bf7", null);

}
`

输入后果:

flatMap 办法和 map 办法相似,不过它反对传入 Optional,应用办法如下所示:

`java
public static void main(String[] args) {

ImportTemplate importTemplate = getImportTemplateById(1);
Optional<String> optionalUrl = Optional.ofNullable(importTemplate)
        .flatMap(f -> Optional.ofNullable(f.getUrl()));
System.out.println(optionalUrl.isPresent());
System.out.println(optionalUrl.get());

}

public static ImportTemplate getImportTemplateById(int id) {

return new ImportTemplate(1, "销售订单 - 普通商品导入模版", "o_w-140e3c1f41c94f238196539558e25bf7", null);

}
`

输入后果:

  1. 总结

对于程序员来说,一不注意就会呈现 NullPointerException 异样,防止它的形式也很简略,比方应用前判断不能为空:

`java
public static void main(String[] args) {

ImportTemplate importTemplate = getImportTemplateById(1);
if (importTemplate != null) {System.out.println(importTemplate.getUrl());
}

}
`

比方为空时,间接返回(或者返回默认值):

`java
public static void main(String[] args) {

ImportTemplate importTemplate = getImportTemplateById(1);
if (importTemplate == null) {return;}
System.out.println(importTemplate.getUrl());

}
`

比方,应用本文中的 Optional。

应用哪种形式不重要,尽可能地防止 NullPointerException` 异样才重要。

  1. 参考

了解、学习与应用 Java 中的 Optional`

退出移动版