您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > JAVA

「译」为什么 null 不好?

时间:2019-09-10 11:14:54  来源:  作者:
  • 原文地址:why null is bad跳转中...
  • 原文作者:Yegor Bugayenko
  • 译者:高老庄里的猿

先来看个 JAVA 中使用 null 作为返回值的简单例子:

「译」为什么 null 不好?

 

该方法最大的问题是返回 null 代替了对象。在面向对象规范中使用 null 是个非常糟糕的做法,应该极力避免。有很多论据可以支持这一观点,包括 Tony Hoare 的演讲《Null References, The Billion Dollar Mistake》和 David West 的《Object Thinking》 整本书。接下来我将所有的论据做一些整理并使用合适的面向对象结构代替 null 作为返回值。目前看来,至少有两种方法可以代替使用 null 。

1、使用空对象设计模式(最好定义一个常量)

「译」为什么 null 不好?

 

2、当不能返回一个对象时,可以抛出异常来让调用方 fail-fast(快速失败)

「译」为什么 null 不好?

 

现在来看看反对使用 null 的依据,除了上面提到的 Tony Hoares 的演讲和 David West 的书籍,还有 Robert Martin 的 《Clean Code》、 Steve McConnell 的《Code Complete》、John Sonmez 的 《Say “No” to “Null”》以及 StackOverflow 上的讨论《Is returning null bad design? 》,这些我在写这篇文章之前都看过。

特殊错误处理

每次将对象引用作为输入时都必须检查它是 null 的还是有效的,如果忘了检查,将会导致运行时 NPE(Null Pointer Exception)。因此,你的代码逻辑会被多种检查和 if/then/else 分支所污染。看看下面例子:

「译」为什么 null 不好?

 

这是 c 语言和其他很多面向过程编程语言处理异常所使用的方法,面向对象编程引入异常处理主要就是为了消除这些特殊的错误处理逻辑。在面向对象编程中,我们将异常以冒泡的方式不断的向上抛出直到应用层,这样我们的代码将变得更加小而美。

dept.getByName("Jeffrey").transferTo(dept2);

null 是面向过程编程的"封建余孽",请使用 null 对象或者异常代替之。

语义的二义性

为了显示的将"函数会返回真实的对象或者 null "这层含义表达出来,getByName() 必须命名为getByNameOrNullIfNotFound()。每个类似的函数都应该这样做,否则会给代码阅读者来带来歧义。

为了语义的准确性,你应该为函数定义更长的名称。

为了消除歧义,函数尽量返回一个真实的对象、一个 null 对象或者抛出一个异常。

有些人会争辩说有时为了性能,不得不返回 null。比如 java Map 接口中的 get() 方法,当在 map 中找不到相应的条目时会返回 null,例如:

「译」为什么 null 不好?

 

由于 Map 的 get() 方法返回 null ,上面代码只会在 map 中搜索一次。如果我们想重写 Map 的 get() 方法以让其在查找不到条目时抛出异常,代码应该这样写:

「译」为什么 null 不好?

 

很明显,这个方法比第一个方法慢两倍,怎么办呢? 我觉得 Map 的接口设计存在缺陷(无意冒犯作者),它应该返回一个迭代器 Iterator 以便让我们代码可以像如下这样:

「译」为什么 null 不好?

 

BTW,这正是 C++ 标准库(STL)中 map::find() 方法的设计思路。

计算机思维 vs 对象思维

假如某人知道 Java 对象是一个指向某个数据结构的指针,并且 知道 null 是一个空指针(在英特尔 x86 处理器中等于 0x00000000),那他应该能接收 if(employee == null) 这个语句。但是,如果以对象思维来进行思考,这个语句就没意义了。 从一个对象的角度来看,我们的代码是这样的:

  • Hello, 请问是软件部吗?
  • 是的。
  • 麻烦让我和你们的 employee(员工) Jeffrey 聊聊。
  • 请稍等...
  • Hello
  • 你是 NULL ?

上面对话的最后一句看起来很奇怪,不是吗? 相反,如果他们在接到我想与 Jeffrey 进行通话的需求后直接挂断电话会快速给我们制造个故障(异常)。这时我们可以尝试着再次拨过去或者直接告诉我们的主管无法联系到 Jeffrey 来完成更大的交易。

或者,他们可以让我们与另一个人交谈,他不是 Jeffrey,但如果我们需要“特定的” Jeffrey(null 对象)的话,他可以帮助我们解决大多数问题,也可以拒绝帮忙。

Slow Failing(慢失败)

与 failing fast(快速失败)相反,上述代码尝试缓慢死亡并杀死其他人。它向调用者隐藏了失败而不是让其知道出了问题需要马上进行异常处理。这个结论与上面"特殊错误处理"章节的讨论很接近。最好让代码尽可能脆弱,必要时让它崩溃。

要确保你的方法对调用方提供的操作数有着极高的要求,如果调用方提供的数据不够或者根本不符合方法主要的使用场景,抛出异常吧。或者返回一个 null 对象,该对象暴露一些常见行为,并对所有其他调用抛出异常,参考如下:

「译」为什么 null 不好?

 

可变的和不完整的对象

一般来说,强烈建议在设计对象时考虑到不变性。这意味着对象在实例化过程中获得所有必要的内容,并且在整个生命周期中永远不会更改其状态。 null 通常被用在延迟加载中以使对象不完整且可变。例如:

「译」为什么 null 不好?

 

这种技术虽然应用广泛,但在面向对象编程中是一种反设计模式的。主要是因为它使一个对象负责计算平台的性能问题,而这对 Employee 对象应该是透明的。

与其管理状态并公开业务相关的行为,不如让对象处理好其自身结果的缓存---这就是延迟加载的目的。缓存不是 employee 该在办公室里做的事,不是吗?

解决办法是不要像上面的例子那样,以这种原始的方式使用延迟加载。相反,将这个缓存问题移到应用程序的另一层。例如在 Java中可以使用面向切面编程技术。 例如,jcabi-aspects 使用 @Cacheable 注解来缓存方法返回的值:

「译」为什么 null 不好?

 

希望通过这篇文章的分析,能让你停止在代码中继续使用 null 作为返回值。

来源:掘金 链接:https://juejin.im/post/5d740e7f5188251325775966



Tags:null   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
Optional类是Java 8新增的一个类,用以解决程序中常见的NullPointerException异常问题。本篇文章将详细介绍Optional类,以及如何用它消除代码中的null检查。 避免使用null检查...【详细内容】
2020-07-30  Tags: null  点击:(27)  评论:(0)  加入收藏
C/C++中,为了避免野指针(即指针没有指向任何地址)的出现,声明一个指针后,最好马上对其进行初始化。如果暂时不明确指针指向哪个变量,则可以赋予NULL,如:int* p = NULL;除了NULL之外...【详细内容】
2020-07-29  Tags: null  点击:(114)  评论:(0)  加入收藏
NULL是SQL常见的关键字之一,表示“空,无”的意思。它在SQL中是一种独特的存在,今天来汇总一下与它相关的知识点,看看这些你都知道吗?先贴一下我们的原始数据,是一个只有1列的表,表...【详细内容】
2020-03-15  Tags: null  点击:(81)  评论:(0)  加入收藏
我们先看下这几个函数具体的用途isset — 检测变量是否已设置并且非 NULLempty — 检查一个变量是否为空,以下的东西都被认作空"" (空字符串)0 (作为整数的0)0.0...【详细内容】
2019-12-30  Tags: null  点击:(76)  评论:(0)  加入收藏
我们已经知道MySQL使用 SQL SELECT 命令及 WHERE 子句来读取数据表中的数据,但是当提供的查询条件字段为 NULL 时,该命令可能就无法正常工作。为了处理这种情况,MySQL提供了...【详细内容】
2019-10-30  Tags: null  点击:(82)  评论:(0)  加入收藏
原文地址:why null is bad跳转中... 原文作者:Yegor Bugayenko 译者:高老庄里的猿先来看个 Java 中使用 null 作为返回值的简单例子: 该方法最大的问题是返回 null 代替了对象...【详细内容】
2019-09-10  Tags: null  点击:(144)  评论:(0)  加入收藏
我们已经知道 MySQL 使用 SQL SELECT 命令及 WHERE 子句来读取数据表中的数据,但是当提供的查询条件字段为 NULL 时,该命令可能就无法正常工作。为了处理这种情况,MySQL提供...【详细内容】
2019-06-20  Tags: null  点击:(434)  评论:(0)  加入收藏
▌简易百科推荐
面向对象的特征之一封装 面向对象的特征之二继承 方法重写(override/overWrite) 方法的重载(overload)和重写(override)的区别: 面向对象特征之三:多态 Instanceof关键字...【详细内容】
2021-12-28  顶顶架构师    Tags:面向对象   点击:(2)  评论:(0)  加入收藏
一、Redis使用过程中一些小的注意点1、不要把Redis当成数据库来使用二、Arrays.asList常见失误需求:把数组转成list集合去处理。方法:Arrays.asList 或者 Java8的stream流式处...【详细内容】
2021-12-27  CF07    Tags:Java   点击:(3)  评论:(0)  加入收藏
文章目录 如何理解面向对象编程? JDK 和 JRE 有什么区别? 如何理解Java中封装,继承、多态特性? 如何理解Java中的字节码对象? 你是如何理解Java中的泛型的? 说说泛型应用...【详细内容】
2021-12-24  Java架构师之路    Tags:JAVA   点击:(5)  评论:(0)  加入收藏
大家好!我是老码农,一个喜欢技术、爱分享的同学,从今天开始和大家持续分享JVM调优方面的经验。JVM调优是个大话题,涉及的知识点很庞大 Java内存模型 垃圾回收机制 各种工具使用 ...【详细内容】
2021-12-23  小码匠和老码农    Tags:JVM调优   点击:(12)  评论:(0)  加入收藏
前言JDBC访问Postgresql的jsonb类型字段当然可以使用Postgresql jdbc驱动中提供的PGobject,但是这样在需要兼容多种数据库的系统开发中显得不那么通用,需要特殊处理。本文介绍...【详细内容】
2021-12-23  dingle    Tags:JDBC   点击:(13)  评论:(0)  加入收藏
Java与Lua相互调用案例比较少,因此项目使用需要做详细的性能测试,本内容只做粗略测试。目前已完成初版Lua-Java调用框架开发,后期有时间准备把框架进行抽象,并开源出来,感兴趣的...【详细内容】
2021-12-23  JAVA小白    Tags:Java   点击:(11)  评论:(0)  加入收藏
Java从版本5开始,在 java.util.concurrent.locks包内给我们提供了除了synchronized关键字以外的几个新的锁功能的实现,ReentrantLock就是其中的一个。但是这并不意味着我们可...【详细内容】
2021-12-17  小西学JAVA    Tags:JAVA并发   点击:(11)  评论:(0)  加入收藏
一、概述final是Java关键字中最常见之一,表示“最终的,不可更改”之意,在Java中也正是这个意思。有final修饰的内容,就会变得与众不同,它们会变成终极存在,其内容成为固定的存在。...【详细内容】
2021-12-15  唯一浩哥    Tags:Java基础   点击:(17)  评论:(0)  加入收藏
1、问题描述关于java中的日志管理logback,去年写过关于logback介绍的文章,这次项目中又优化了下,记录下,希望能帮到需要的朋友。2、解决方案这次其实是碰到了一个问题,一般的情况...【详细内容】
2021-12-15  软件老王    Tags:logback   点击:(19)  评论:(0)  加入收藏
本篇文章我们以AtomicInteger为例子,主要讲解下CAS(Compare And Swap)功能是如何在AtomicInteger中使用的,以及提供CAS功能的Unsafe对象。我们先从一个例子开始吧。假设现在我们...【详细内容】
2021-12-14  小西学JAVA    Tags:JAVA   点击:(22)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条