前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >​JDK1.8 新特性 (八):还在重复写空指针检查代码?​

​JDK1.8 新特性 (八):还在重复写空指针检查代码?​

作者头像
xcbeyond
发布于 2020-08-22 10:57:33
发布于 2020-08-22 10:57:33
1.6K00
代码可运行
举报
文章被收录于专栏:技术那些事技术那些事
运行总次数:0
代码可运行

1、前言

作为一名Java程序员,无论是初入茅庐的菜鸟,还是久经江湖的高手,曾经肯定遭遇过各种各样的异常错误。在国外的一篇文章中,就统计了关于异常类型的排行榜,如下图:

是的,你没有看错,NullPointerException位居榜首。

Null Reference的发明者Charles Antony Richard Hoare说过:

“我称之为我的十亿美元错误。这是1965年发明空引用的结果……这导致了无数的错误,漏洞和系统崩溃,在最近40年中可能造成十亿美元的痛苦和破坏。”

这看起来有些夸张,但毫无争议的是NullPointerException简直就是程序员心中的痛,并不是说它有多难以解决,而是为了解决它我们需要再付出了额外代价。String name = "Unknown";if (null != people) { if (null != people.getName()) { name = people.getName(); }}return name;

还记得当初刚入行时候的你,三天两头碰到NullPointerException而引发的bug,解决完一个,又在另一个地方遇到。这也慢慢让你懂得,不要相信任何“对象”,特别是别人提供给你的,在使用的地方都加上判断,这样就放心多了。于是代码通常就变成了下面这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
String name = "Unknown";
if (null != people) {
  if (null != people.getName()) {
      name = people.getName();
    }
}
return name;

这样处理,虽然不用担心NullPointerException了,但是过多的判断语句着实让人头皮发麻,代码变得臃肿不堪。如果对象过于复杂,对象里面还有对象等等,你还要继续逐层判断么?

令人兴奋的是,JDK1.8引入了一个新类java.util.Optional<T>,凭借Optional类提供的API,我们再也不用担心NullPointerException了,更不会再去写那些烦人的判断啦。

2、Optional类

举例来说,使用新类意味着,如果你知道一个人可能有也可能没有车,那么Person类内部的car变量就不应该声明为Car,遭遇某人没有车时把null引用赋值给它,而是应该像下图这样直接将其声明为Optional<Car>类型。

变量存在时,Optional类只是对类简单封装。变量不存在时,缺失的值会被建模成一个“空”的Optional对象,由方法Optional.empty()返回。Optional.empty()方法是一个静态工厂方法,它返回Optional类的特定单一实例。

Optional,本质上是一个容器对象,拥有一个非空值或空值,需要我们将对象实例传入该容器中。如果值存在,Optional.isPresent()方法返回true,并通过Optional.get()方法获取值。

Optional的构造方法为private,无法直接使用new来创建Optional对象,只能使用Optional提供的静态方法创建。

Optional提供的创建方法如下:

  • Optional.of(obj):如果对象为 null,将会抛出NullPointerException
  • Optional.ofNullable(obj):如果对象为 null,将会创建不包含值的 EMPTY Optional对象实例(new Optional<>())。
  • Optional.empty() :等同于 Optional.ofNullable(null)。

其中,源码片段如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * Constructs an instance with the value present.
 *
 * @param value the non-null value to be present
 * @throws NullPointerException if value is null
 */
private Optional(T value) {
    this.value = Objects.requireNonNull(value);
}

……

/**
 * Returns an {@code Optional} with the specified present non-null value.
 *
 * @param <T> the class of the value
 * @param value the value to be present, which must be non-null
 * @return an {@code Optional} with the value present
 * @throws NullPointerException if value is null
 */
public static <T> Optional<T> of(T value) {
    return new Optional<>(value);
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * Returns an {@code Optional} describing the specified value, if non-null,
 * otherwise returns an empty {@code Optional}.
 *
 * @param <T> the class of the value
 * @param value the possibly-null value to describe
 * @return an {@code Optional} with a present value if the specified value
 * is non-null, otherwise an empty {@code Optional}
 */
public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * Common instance for {@code empty()}.
 */
private static final Optional<?> EMPTY = new Optional<>();

……

/**
 * Returns an empty {@code Optional} instance.  No value is present for this
 * Optional.
 *
 * @apiNote Though it may be tempting to do so, avoid testing if an object
 * is empty by comparing with {@code ==} against instances returned by
 * {@code Option.empty()}. There is no guarantee that it is a singleton.
 * Instead, use {@link #isPresent()}.
 *
 * @param <T> Type of the non-existent value
 * @return an empty {@code Optional}
 */
public static<T> Optional<T> empty() {
    @SuppressWarnings("unchecked")
    Optional<T> t = (Optional<T>) EMPTY;
    return t;
}

强烈建议使用Optional.ofNullable(obj)方法,来创建Optional对象,并获取对应值。

3、Optional的使用

到目前为止,你已经知道Optional的好处了吧,但是,我们该如何使用呢?

使用可接受null的Optional对象,即:使用静态工程方法Optional.ofNullable(obj),创建一个可以允许null值的Optional对象:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Optional<People> optional = Optional.ofNullable(people);

即使people是null,optional对象也就是个空对象。

如果people不为null,根据Optional.isPresent()方法返回true,并通过Optional.get()方法获取值。

为了避免NPE,Optional.isPresent()方法已经对null进行了判断,若存在返回true。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
People p = null;if (optional.isPresent()) {    p = optional.get();}

看到这里,你可能会发现这与null判断检查并无差异。

后来接触到Optional其他API,我才发现真正体现它价值的是下面这些API。

3.1 Optional.map

从对象中获取某个属性,是最常见的操作。比如,你可能需要从people对象中获取人名。在获取人名之前,你需要检查people对象是否为null,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
String name = null;
if (null != people) {
    name = people.getName();
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
String name = null;if (null != people) {    name = people.getName();}

使用Optional.map方法,可以这么写:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Optional<People> optional = Optional.ofNullable(people);Optional<String> stringOptional = optional.map(People::getName);

3.2 Optional.orElse

当一个对象为 null 时,业务上通常可以设置一个默认值,从而使流程继续下去。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
String name = null != people ? people.getName() : "Unknown";
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
或者抛出一个异常。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (null != people.getName()) {    throw new RuntimeException();}

Optional 类提供两个方法 orElseorElseThrow ,可以方便完成上面转化。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

// 设置默认值
String name = optional.orElse(new People("Unknown")).getName();
// 抛出异常
String name = optional.orElseThrow(RuntimeException::new).getName();

如果 optional 为空,提供默认值或抛出异常。

3.3 Optional.filter

你经常需要调用某个对象的方法,查看它的某些属性。比如,你可能需要检查人名是否为“xcbeyond”。为了以一种安全的方式进行这些操作,你首先需要判断people对象是否为null,再调用它的方法getName,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (null != people && "xcbeyond".equals(people.getName())) {
  System.out.println("ok");
}

使用Optional类提供的方法filter,可以很好的重构:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
optional.filter(people1 -> "xcbeyond".equals(people.getName()))
    .ifPresent(x -> System.out.print("ok"));

4、Optional重构代码

让我们一起再看看文章开头的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
String name = "Unknown";
if (null != people) {
  if (null != people.getName()) {
      name = people.getName();
    }
}
return name;

在知道了Optional之后,进行代码重构:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Optional<People> optional = Optional.ofNullable(people);
return optional.map(People::getName).orElse("Unknown");
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序猿技术大咖 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
ID转换大全
实际上掌握了编程的思维,任何一门语言都可以做id转换! 对于初学者来说,这个是非常实用的一个,很多人当初就是因为要做这个转换,才慢慢走入了编程的道路。 使用大部分软件的时候,第一步就是文件数据准备,基本上都是数据的拆分和整合,这个拿id转换做基础练习也挺好的! 本来应该作为第一讲,但是当初认为太基础了,而忽略掉了,放在这里也好,大部分同学已经跟我们学习两个月了,可以拿这个题目来检验自己的水平了! ID转换简单来说,就是找到对应关系表,然后用hash或者字典对应一下即可。但也可以很复杂: 为什么要转换id?
生信技能树
2018/03/08
2.8K0
ID转换大全
生信中各种ID转换
1.Uniprot ID mapping 可以很方便地把 ID 转换为其他 ID 类型, 所包含的类型十分全面【https://www.uniprot.org/uploadlists/】
DoubleHelix
2020/06/04
11.3K1
生信中各种ID转换
生信技能树 Day8 9 GEO数据挖掘 基因芯片数据
有时eSet里面有两个对象,可以到网页看一下,可能是因为测了两种芯片,我们分开分析就好。
用户11064093
2024/04/19
4430
输出人类全部基因的全名和别名
如果你通过数据分析拿到了一系列感兴趣的基因,但是只有类似于TP53这样的基因标准symbol名字,想批量拿到全部的基因的全名和别名,这里有一个代码分享给大家。
生信技能树
2023/02/28
5870
输出人类全部基因的全名和别名
探针注释之其他基因id转换
注意看:if you get error by this code ,please try different type parameters
用户11414625
2024/12/20
1090
探针注释之其他基因id转换
GEO数据挖掘-2
GEO数据挖掘—2 四、代码分析流程 1. 下载数据并从中提取有用信息 gse_number = "GSE56649" eSet <- getGEO(gse_number, destdir = '.', getGPL = F) #(1)提取表达矩阵exp exp <- exprs(eSet) dim(exp) exp[1:4,1:4] 关于表达矩阵里的负值 取过log,有负值 —— 正常 没取过log,有负值 ——错误数据 有一半负值 ——做了标准化 获取实验分组和探针注释 # 生成Grou
大胖橘
2023/03/18
8180
三阴性乳腺癌表达数据分析笔记之PAM50
取出PAM50基因,根据这些基因的表达了绘制热图,并添加分组信息,与原始分组(TNBC,noTNBC)进行对比。
生信技能树
2020/10/26
3.3K0
三阴性乳腺癌表达数据分析笔记之PAM50
GEO多数据集联合分析-文献复现
文献题目:基于生物信息学的新型铁死亡基因生物标志物和免疫浸润谱在糖尿病肾病中的应用Huang, Y., & Yuan, X. (2024). Novel ferroptosis gene biomarkers and immune infiltration profiles in diabetic kidney disease via bioinformatics. FASEB journal : official publication of the Federation of American Societies for Experimental Biology, 38(2), e23421. https://doi.org/10.1096/fj.202301357RR. IF: 4.8 Q1
用户11008504
2024/05/11
3752
GPL14877、GPL570、hgu133plus2.db 比较
但是,我在在利用hgu133plus2.db进行探针名转换为基因名时出现问题 ,代码如下:
生信技能树
2020/12/03
3.2K0
GPL14877、GPL570、hgu133plus2.db 比较
第一个万能芯片探针ID注释平台R包
首先,我们说官网,肯定可以找到,不然这种芯片出来就没有意义了!然后,我们看看NCBI下载的,会比较大
生信技能树
2019/12/05
13.7K0
什么基因研究最多??
如果我们想探索一下什么基因研究的最多,那就是检索pubmed数据库资源。在 NCBI的ftp里面关于人的一些基因信息 :
DoubleHelix
2022/01/17
4270
什么基因研究最多??
生信 | 利用Bioconductor包注释探针,进行探针ID转换
不同的GPL进行注释所需要用到的R包是不同的,我们首先要明白我们的GPL应该用什么R包
LN生物笔记
2023/02/23
2.5K0
你的ID转换错啦
这个基本上不太可能啊,GAPDH和ACTB这两个都是比较常见的高表达量基因,不应该是这样的数值 !
生信技能树
2021/05/27
7610
你的ID转换错啦
使用R语言获取人类所有基因的名字,ID,symbol以及别名
然后直接把下面的代码运行一下,把输出的all_gene_bioconductor.html文件好好看看, 就明白了。
生信技能树
2018/07/27
3.6K0
使用R语言获取人类所有基因的名字,ID,symbol以及别名
【资源分享】生物信息学编程实战
市面上唯一适合生物信息学从业者的教学视频 直接复制链接 https://ke.qq.com/course/285055 到浏览器即可打开购买 永不打折,但是会下架,请抓紧机会购买! 编程这个技能,随着
生信技能树
2018/06/07
3.9K0
学徒笔记——芯片数据的注释文件获取
写文章确实是个严谨的事,但是万一呢,有时候做个脑瘤的分析整个糖尿病的编号在里面,也是大受震撼,一般来说起码都是一个物种的,平台一不一致问题不大的样子。通篇检查一下,可能就是差那么一位数,但是一定有写对的地方。
生信技能树
2021/07/06
4.5K0
GEO数据挖掘-基于芯片
在require()函数中,如果直接传递包的名称作为参数,不需要加引号;如果包的名称以字符串形式存储在变量中,则需要使用character.only = TRUE来指定这个变量是一个字符串
sheldor没耳朵
2024/07/23
3280
GEO数据挖掘-基于芯片
R语言练习题10道,有答案代码,还有视频
根据R包org.Hs.eg.db找到下面ensembl 基因ID 对应的基因名(symbol)
生信技能树
2018/12/27
1.4K0
生信马拉松 Day9-10 GEO数据分析笔记
今天正式开始教画图了,具体的代码其实挺多地方讲到了,上课的好处就是可以听到很多细节和经验,是自己零散地找资料不能相比的,收获很多,感觉要全部吞下来还要再复习几遍
阿呆的月历
2024/01/26
2721
(16)芯片探针与基因的对应关系-生信菜鸟团博客2周年精选文章集
这个我非常喜欢,目录如下: 用R获取芯片探针与基因的对应关系三部曲-bioconductor 用R获取芯片探针与基因的对应关系三部曲-NCBI下载对应关系 gene的各种ID转换终结者-bioconductor系列包 现有的基因芯片种类不要太多了! 但是重要而且常用的芯片并不多! 一般分析芯片数据都需要把探针的ID切换成基因的ID,我一般喜欢用基因的entrez ID。 一般有三种方法可以得到芯片探针与gene的对应关系。 金标准当然是去基因芯片的厂商的官网直接去下载啦!!! 一种是直接用bioconduc
生信技能树
2018/03/08
6K0
推荐阅读
相关推荐
ID转换大全
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验