上周,我们就在应用程序的服务层中处理null产生了激烈的争论。问题是在.NET上下文中,但在Java和许多其他技术中将是相同的。

问题是:无论什么情况下,您是否应始终检查null并使代码工作,或者在我看来,当意外收到null时,让异常冒泡吗?

在我看来,在不希望它存在的地方(即没有用户界面来处理它)检查null就像编写带有空catch的try块一样。您只是隐藏一个错误。错误可能是代码中的某些内容已更改,并且null现在是预期值,或者存在其他错误,并且将错误的ID传递给该方法。

另一方面,检查通常,null可能是一个好习惯。此外,如果进行检查,则应用程序可能会继续运行,而只有一小部分功能无效。然后,客户可能会报告一个小错误(例如“无法删除评论”),而不是更严重的错误(例如“无法打开页面X”)。

您将遵循哪种做法,您对这两种方法的支持或反对意见是什么方法?

更新:

我想添加一些有关我们特定案例的细节。我们正在从数据库中检索一些对象,并对它们进行了一些处理(比如,建立一个集合)。编写代码的开发人员没有想到该对象可以为null,因此他不包含任何检查,并且在加载页面时出现错误,并且整个页面都没有加载。

显然,在这种情况下,应该进行检查。然后,我们讨论了是否应该检查每个处理的对象(即使不会丢失),以及是否应该静默中止最终处理。

假设的好处是该页面将继续工作。考虑一下Stack Exchange上不同组(用户,评论,问题)的搜索结果。该方法可以检查是否为空,并中止用户的处理(由于错误为空),但是返回“评论”和“问题”部分。该页面将继续工作,只是缺少“用户”部分(这是一个错误)。我们应该尽早失败并中断整个页面,还是继续工作并等待有人注意到“用户”部分丢失了?

评论

福克斯·穆德(Fox Mulder)的原则:不要相信任何人。

@YannisRizos:这个原则是一个很好的原则-太好了,Java和C#的创建者让语言运行时环境自动完成了这一工作。因此,通常不需要在代码中使用这些语言对各处的null进行显式检查。

方法的可能重复项是否应验证其参数?

assert(foo!= null,“ foo是转发器中的Web控件,没有理由期望它为null,等等,等等...”);

开发时抛出异常。在运输时隐藏异常。为此使用断言。.

#1 楼

问题不仅仅在于您是否应该检查null还是让运行时抛出异常。

那么,您的选择是:


引发通用异常(NullReferenceException)并使其冒泡向上;如果您不进行自我检查,这将自动发生。
抛出一个自定义异常,从更高层次描述问题;这可以通过抛出null检查或捕获null并抛出更具体的异常来实现。
通过替换一个合适的默认值进行恢复。

没有通用的规则一种是最好的解决方案。我个人会说:


如果错误条件表明代码中存在严重错误,则通用异常最好,也就是说,永远不应该将null值设为首先可以到达那里。这样的异常可能会一直扩展到您设置的任何错误日志中,以便有人得到通知并修复该错误。
如果提供半有用的输出比正确性更重要,则默认值解决方案是很好的,例如网络浏览器,它接受技术上不正确的HTML并尽最大努力呈现出来。
否则,我将使用特定的异常并在适当的地方进行处理。


评论


对于最终用户@Stilgar没有区别。对于开发人员,特定的异常(例如“无法访问数据库服务器”)比“空指针异常”更直观

– k3b
2012年5月7日在7:16

尽早而艰难地失败意味着,线路发生更细微的错误,错误的身份验证,损坏的数据持续存储,信息泄漏以及其他有趣的事情的风险降低了。

–拉克斯·维克伦德
2012年5月7日12:14

@Stilgar:这里有两个问题:无人看管的行为和可维护性。虽然对于无人值守的行为,特定的异常与通用的异常之间没有什么区别,但是对于可维护性,它可以使“哦,这就是为什么事情会崩溃”与数小时的调试马拉松之间的区别。

– tdammers
2012年5月7日19:21

tdammers是完全正确的。 “没有将对象引用设置为对象的实例”是我最频繁看到的最烦人的异常之一。我宁愿看到一个特定的异常,无论它是带有参数名称的ArgumentNullException还是自定义异常“用户TK421不存在任何发布记录”。

– Sean Chase
2013年12月8日17:00

@ k3b对于最终用户而言,“数据库服务器不可访问”也非常有帮助。至少,对于任何对常见问题有一些了解的最终用户-可能会帮助他发现电缆松动,路由器需要重新启动等,而不必为有bug的软件感到生气。

–朱莉娅·海沃德(Julia Hayward)
2015年2月3日,9:52

#2 楼

恕我直言,尝试处理您不期望的空值会导致代码过于复杂。如果您不希望为null,请抛出ArgumentNullException使其清楚。当人们检查该值是否为null,然后尝试编写一些毫无意义的代码时,我会感到非常沮丧。当有人真的期望SingleOrDefault时,使用Single(甚至更糟的是获得收藏)也是如此,而在许多其他情况下,人们则害怕(我不知道是什么)清楚地陈述其逻辑。

评论


应该使用代码协定,而不是ArgumentNullException(除非它是不支持代码协定的2010年前的源代码)。

– Arseni Mourzenko
2012年5月6日18:40



我同意你的看法。如果不期望为null,则不应以奇怪的方式对其进行处理,而应仅进行报告。

–乔治
2012年5月6日18:56

如果我没看错这个问题,则抛出ArgumentNullException视为“处理”空值:它可以防止以后出现空引用异常。

– KutuluMike
2012年5月6日在21:19

@MichaelEdenfield:根据给定的问题,我认为这不算是真正的处理(我们的目标是无论如何都不使您的代码正常工作)。老实说,我经常忘记编写适当的代码块,如果传递null会抛出该代码块。但是,我认为抛出NullArgumentException是最佳实践,而我认为最糟糕的做法是编写无法处理null而是抛出异常返回的代码,例如作为方法结果的另一个null(或空字符串,空集合或-1,或者如果返回类型为void则跳过方法执行等)。

–empi
2012年5月6日在21:53



@ Giorgio,ArgumentNullException可以非常清楚地说明问题是什么以及如何解决。 C#不会倾向于使用“未定义”。如果您以未定义的方式调用某些内容,则您的调用方式没有任何意义。例外是表明这一点的正确方法。现在,有时我将使解析方法在无法解析int的情况下返回null。但是在这种情况下,无法解释的结果是合理的可能性而不是使用错误。另请参阅本文。

– Kyralessa
2012年5月7日13:51

#3 楼

根据我的经验检查null的习惯来自以前的C或C ++开发人员,在这些语言中,如果不检查NULL,则很有可能隐藏严重错误。如您所知,在Java或C#中情况有所不同,在不检查null的情况下使用null ref将导致异常,因此只要您在调用端不忽略该错误,该错误就不会被秘密隐藏。因此,在大多数情况下,显式检查null没有多大意义,并且会使代码过于复杂。这就是为什么我不认为空检查在这些语言中是个好习惯(至少不是一般而言)。

当然,该规则有例外,以下是我认为的例外作者:


您想要一个更好的,易于理解的错误消息,告诉用户在程序的哪个部分中出现了错误的空引用。因此,您对null的测试只会引发不同的异常,并带有不同的错误文本。显然,这样不会掩盖任何错误。
您想让程序“更早地失败”。例如,您要检查构造函数参数的null值,否则对象引用将仅存储在成员变量中,以后再使用。
您可以安全地处理null ref的情况,而无需具有后续故障的风险,并且不会掩盖严重错误。
您希望在当前上下文中使用完全不同的错误信号(例如,返回错误代码)


评论


“您想要一个更好的,易于理解的错误消息”-我认为这是做到这一点的最佳理由。 “对象引用未设置为对象的实例”或“空引用异常”从没有像“产品未实例化”那样有用。

– pdr
2012年5月6日16:31

@pdr:检查的另一个原因是要确保始终检测到它。

–乔治
2012年5月6日18:57

也许是“前C开发人员”。仅当这些人自己正在恢复C开发人员时,才可以“以前的C ++开发人员”。作为一名现代C ++开发人员,我对使用裸指针或鲁re动态分配的代码非常怀疑。实际上,我很想扭转论点,说C ++不会遭受OP所描述的问题,因为它的变量没有Java和C#具有的其他特殊的null-ness属性。

– Kerrek SB
2012年5月6日21:47

您的第一段只是故事的一半:是的,如果在C#中使用null引用,则将获得异常,而在C ++中,将获得未定义的行为。但是,在编写良好的C#中,您所拥有的空值引用要比编写良好的C ++多得多。

–詹姆斯·麦克奈利斯
2012年5月6日22:33

封装越好,这个答案就越适用。

– radarbob
2015年10月11日在20:21

#4 楼

使用断言测试并指示代码的前置条件/​​后置条件和不变式。

它使理解代码期望和期望处理的内容变得更加容易。 />断言(IMO)是合适的检查方法,因为它有效:


高效(通常不用于发布),
简短(通常为一行)和
清除(违反先决条件,这是一个编程错误)。

我认为自记录代码很重要,因此进行检查很不错。防御性的,快速故障的编程使维护方式更容易,这通常是大多数时间所花费的时间。

Update

详细说明。通常最好向最终用户提供可以在错误(即尽可能多地显示)下运行的应用程序。健壮性很好,因为天堂只知道在关键时刻会破裂。

但是,开发人员和测试人员必须尽快意识到错误,因此您可能想要一个带有钩子的日志记录框架,该钩子可以显示一个错误。提醒用户存在问题。该警报可能应该显示给所有用户,但可能会根据运行时环境进行不同的调整。

评论


断言发布中不存在对我来说是一个很大的负面因素,发布是您想要添加信息的确切时间

– jk。
2015年2月3日在10:48



好吧,如果需要的话,也可以使用在发布中处于活动状态的assert ^ h ^ h ^ h条件抛出函数。我经常自己动手。

– Macke
2015年2月3日,19:30



#5 楼

早期失败,经常失败。 null是可以获取的最好的意外值之一,因为一旦尝试使用它,很快就会失败。其他意外值不太容易检测。由于null每当您尝试使用它时都会自动出现故障,因此我想说它不需要显式检查。

评论


我希望你的意思是:及早失败,快速失败,不常发生...

–empi
2012年5月6日16:56

问题在于,如果没有显式检查,则null值有时可能会在导致异常之前传播很远,然后很难找出它的起源。

–迈克尔·伯格沃德(Michael Borgwardt)
2012年5月6日16:57

@MichaelBorgwardt这不是堆栈跟踪的目的吗?

–R0MANARMY
2012年5月8日20:28

@ R0MANARMY:当将null值保存到某个字段并且NullPointerException抛出得多以后,堆栈跟踪无济于事。

–迈克尔·伯格沃德(Michael Borgwardt)
2012年5月8日20:30

@ R0MANARMY,即使您能够信任堆栈跟踪中的行号(在许多情况下也不能),某些行包含多个可能为空的变量。

– Ohad Schneider
17年7月5日在12:22

#6 楼


检查空值应该是您的第二天性
您的API将被其他人使用,并且他们的期望可能与您的不同
全面的单元测试通常会强调需要空值引用check
检查null并不意味着条件语句。如果您担心空检查会降低代码的可读性,那么您可能需要考虑.NET代码协定。
考虑安装ReSharper(或类似工具)以在代码中搜索缺失的空引用检查


评论


使用代码协定作为前提条件只是条件条件语句的美化。

–用户
2012年5月7日,11:28

是的,我同意,但是我也认为使用代码协定时代码看起来更干净,即提高了可读性。

– CodeART
2012年5月7日晚上11:30

#7 楼

我的一般方法是永远不要测试您不知道如何处理的错误情况。然后,在每个特定实例中需要回答的问题变成:在出现意外的null值的情况下,您可以做一些合理的事情吗?例如默认值或实例),那么请务必这样做。 @Shahbaz在《魔兽世界》中将一个图像替换为另一个图像的例子属于该类别。图像本身可能只是装饰,没有功能影响。 (在调试版本中,我将使用尖叫的颜色来引起注意替换已发生的事实,但这在很大程度上取决于应用程序的性质。)但是,请记录有关该错误的详细信息,以便您知道该错误的发生。否则,您将隐藏您可能想知道的错误。对用户隐藏它是一回事(同样,假设处理可以安全地继续);

如果您在存在null值的情况下不能安全地继续操作,则会失败,并出现明智的错误;通常,我倾向于在操作无法成功的情况下尽快失败,这意味着检查前提条件。有时您不希望立即失败,而要尽可能长地推迟失败。例如,在与密码相关的应用程序中会出现这种考虑,在这种应用程序中,旁通道攻击可能会泄露您不希望知道的信息。最后,让错误冒泡到一些通用错误处理程序,该错误处理程序再记录相关的详细信息,并向用户显示一个不错的(无论多么不错)错误消息。

还要注意的是,测试/ QA /调试版本与生产/发行版本之间的正确响应可能会有很大差异。在调试版本中,我会尝试尽早失败,并给出非常详细的错误消息,以使其完全清楚地表明存在问题,并允许事后确定确定需要解决的问题。遇到安全可恢复的错误时,生产版本可能应该支持恢复。

评论


当然,在链上有通用的错误处理程序。日志的问题在于很长一段时间内可能没有人检查它们。

– Stilgar
2012年5月7日下午16:45

@Stilgar,如果没有人可以检查日志,那么存在或不存在空值检查都不是问题。

–用户
2012年5月8日在7:35

这儿存在一个问题。如果有空检查只是简单地隐藏错误并将其写入日志,则最终用户可能不会注意到系统长时间无法正常运行。

– Stilgar
2012年5月8日在7:42

@Stilgar,这完全取决于错误的性质。正如我在帖子中所说的,只有您可以安全地继续操作,才应替换/忽略意外的null。例如,在发行版本中使用一个映像而不是另一个映像(在调试版本中,尽早并尽可能地失败,以使问题变得明显)是与返回无效结果进行计算相比非常不同的野兽。 (编辑后的答案包括在内。)

–用户
2012年5月8日7:50



除非您能以某种方式记录并报告错误(而不必过于依赖精明的用户),否则我会尽早快速失败,甚至在生产中也会失败。隐藏用户部署的错误是一回事-将它们隐藏起来很危险。同样,允许用户忍受细微的错误可能会削弱他们对您的应用程序的信心。有时最好咬一下子弹,让他们知道有一个错误,并且您要尽快解决。

– Merlyn Morgan-Graham
2012年5月21日在9:24



#8 楼

我将从语义的角度考虑问题,即问自己一个NULL指针或一个空引用参数代表什么。

如果函数或方法foo()的参数x为T类型,x可以是强制性的也可以是可选的。

在C ++中,您可以将强制性参数作为



一个值进行传递,例如void foo(T x)
参考,例如void foo(T&x)。在两种情况下,您都没有检查NULL指针的问题。因此,在许多情况下,根本不需要对强制性参数进行空指针检查。

如果传递的是可选参数,则可以使用指针并使用NULL值指示否。值已给出:

void foo(T* x)


替代方法是使用智能指针,如

void foo(shared_ptr<T> x)


,您可能想检查代码中的指针,因为这对方法foo()有所不同,x是否包含值或根本没有值。

第三种也是最后一种情况是您使用了强制性
参数的指针。在这种情况下,使用x == NULL调用foo(x)是一个错误,并且
您必须决定如何处理此错误(与使用
越界索引或任何无效输入相同) ):


不检查:是否有错误让它崩溃,并希望该错误足够早地出现(在测试过程中)。如果不是这样(如果您没有足够早地失败),则希望它不会出现在生产系统中,因为用户体验将是他们看到应用程序崩溃。
请在开始时检查所有参数。方法并报告无效的NULL参数,例如从foo()引发异常,然后让其他方法处理它。可能最好的办法是向用户显示一个对话框,显示该应用程序遇到内部错误并将要关闭。

除了更好的失败方法(向用户提供更多信息)之外,第二种方法的优点是更可能发现错误:每次使用错误的参数调用方法时,程序都会失败方法1该错误仅在执行取消引用指针的语句后才会出现(例如,如果输入了if语句的右分支)。因此,方法2提供了一种更强的“早期失败”形式。

在Java中,您有较少的选择,因为所有对象都是使用始终为null的引用传递的。同样,如果null表示可选参数的NONE值,则检查null(可能)将成为实现逻辑的一部分。

如果null是无效的输入,那么您将遇到错误并且我将采用与C ++指针的情况类似的考虑。由于在Java中您可以捕获空指针异常,因此,如果方法foo()取消了所有执行路径中的所有输入参数,则上述方法1等同于方法2(在小型方法中可能经常发生)。





在C ++和Java中,检查可选参数是否为null都是程序逻辑的一部分。
在C ++中,我将始终检查强制性参数以确保抛出适当的异常,以便程序能够以健壮的方式对其进行处理。
在Java(或C#)中,如果存在以下情况,我会检查强制性参数:有一些执行路径不会为了使“早期失败”具有更强的形式。

EDIT

感谢Stilgar的帮助有关详细信息。

您的情况似乎是方法的结果为null。同样,我认为您应该先确定(并修复)方法的语义,然后再做出决定。

因此,您有方法m1()调用方法m2()和m2()返回对某个对象的引用
(在Java中)或指针(在C ++中)。

m2()的语义是什么?
m2()是否应始终返回非空结果?如果是这种情况,那么结果为null就是内部错误(在m2()中)。
如果在m1()中检查返回值,则可以得到更可靠的错误处理。如果不这样做,迟早会有例外。也许不吧。希望在测试期间而不是部署之后引发异常。问题:如果m2()永远不要返回null,为什么它返回null?也许应该抛出一个异常呢? m2()可能有问题。

替代方法是,返回null是方法m2()的语义的一部分,即它具有可选的结果,应将其记录在Java2的Javadoc文档中。方法。在这种情况下,可以为空值。方法m1()应该将其检查为其他任何值:这里没有错误,但可能检查结果是否为null只是程序逻辑的一部分。

评论


在没有情况的情况下,不是参数为空。我们进行了数据库调用,以获取预期存在但实际上不存在的内容。问题是我们是否应该检查每个对象是否存在,或者仅在我们期望它丢失时才应该检查。

– Stilgar
2012年5月6日18:05

因此,方法返回的结果是对对象的引用,而null是用于表示结果的值:NOT FOUND。我理解正确吗?

–乔治
2012年5月6日18:07



我已用其他详细信息更新了问题。

– Stilgar
2012年5月6日18:26

#9 楼

肯定不会出现NULL,但是总会有bug!

我相信您应该检查NULL是否不期望它。让我给你举个例子(尽管它们不是用Java编写的)。



在魔兽世界中,由于一个错误,一个开发人员的形象出现在一个立方体上对于许多对象。想象一下,如果图形引擎不期望使用NULL指针,那么将会发生崩溃,而对于用户而言,它却不是那么致命(甚至很有趣)。至少是,您不会意外失去连接或任何保存点。

这是一个检查NULL防止崩溃的情况,该情况被静默处理。 >

想象一个银行出纳员程序。该接口进行一些检查,并将输入数据发送到基础部分以执行汇款。如果核心部分不希望收到NULL,那么界面中的错误可能会导致交易出现问题,中间的一些钱可能会丢失(或更糟的是重复)。

通过“ a问题”是指崩溃(例如崩溃(例如,程序是用C ++编写的),而不是空指针异常)。在这种情况下,如果遇到NULL,则事务需要回滚(如果不对NULL进行变量检查,那当然是不可能的!)


您就会明白。

检查函数参数的有效性不会使您的代码混乱,因为这是对函数顶部的几次检查(中间没有进行检查),并且大大增加了坚固性。

如果您必须在“程序告诉用户它已失败并正常关闭或回退到可能创建错误日志的一致状态”和“访问冲突”之间进行选择,您是否会选择第一个?这意味着每个功能都应该为错误代码调用做好准备,而不是仅仅因为错误总是存在而期望一切都正确。

评论


您的第二个例子证明了您的观点。在银行交易中,如果出现任何问题,交易应中止。恰恰在您掩盖错误时,金钱才可能丢失或重复。检查空值就像“客户汇出零钱...好吧,我将汇出10美元(相当于开发者的图像)”

– Stilgar
2012年5月7日在6:23

@Stilgar,相反!检查NULL将中止并显示消息。不检查NULL将导致崩溃。现在,崩溃在交易开始时可能还不错,但在交易中间可能会带来灾难性的后果。 (我并不是说银行转帐应该像游戏一样处理错误!只是应该处理它的事实)

– Shahbaz
2012年5月7日在8:38

“事务”的全部要点是它不能完成一半:)如果有错误,则将其回滚。

– Stilgar
2012年5月7日16:33

对于实际上需要事务语义的任何事情,这都是可怕的建议。您应该使所有事务都是原子的和幂等的,否则,您所犯的任何错误都将保证资源的丢失或重复(金钱)。如果必须处理非原子或非幂等操作,则应非常小心地将其包装在保证原子和幂等行为的层中(即使您必须自己编写这些层)。一直想:如果在进行此交易时流星撞击Web服务器会发生什么?

– Merlyn Morgan-Graham
2012年5月21日在9:11



@ MerlynMorgan-Graham,我这样做了。希望能消除混乱。

– Shahbaz
2012年5月22日上午9:33

#10 楼

如其他答案所示,如果您不期望NULL,请抛出ArgumentNullException以使其清楚。我认为,在调试项目时,它可以帮助您更快地发现程序逻辑中的错误。

因此,如果要恢复这些NullRefrences检查,您将要发布软件。不要错过软件逻辑上的任何内容,它可以确保不会弹出严重的错误。

另一点是,如果对函数的参数进行NULL检查,然后,您可以确定该功能是否是执行此操作的正确位置;如果不是,请找到对函数的所有引用,然后在将参数传递给函数之前检查可能导致空引用的情况。

#11 楼

系统中的每个null都是一个定时炸弹,等待被发现。在代码与不受控制的代码进行交互的边界处检查null,并在您自己的代码中禁止null。这使您摆脱了问题,而不必天真地信任外来代码。请改用例外或某种Maybe / Nothing类型。

#12 楼

尽管最终会抛出一个空指针异常,但它通常会与您获得空指针的位置相距甚远。至少要记录下您尽快混入空指针这一事实,以帮自己一个忙,并缩短修复错误所需的时间。

即使您现在不知道该怎么办,记录事件将节省您以后的时间。也许获得票证的人将能够使用节省的时间来找出正确的响应。

#13 楼

由于@DeadMG(@empi)指出的Fail early, fail fast是不可行的,因为Web应用程序必须能够在错误下正常运行
,您应该强制执行规则

不进行日志记录就不能捕获异常

因此,作为开发人员,您可以通过检查日志来了解潜在的问题。

如果您使用的是log4net或log4j之类的日志记录框架,则可以设置其他特殊的日志记录输出(又称为附加器)将错误和致命错误邮寄给您。因此,您可以随时了解最新信息。

[更新]

如果页面的最终用户不应该知道该错误,则可以设置一个附加程序
,向您发送错误消息和致命错误,您可以随时了解。

如果页面的最终用户可以看到隐秘的错误消息,则可以设置一个附加程序,将页面处理的错误日志放在页面的末尾。

无论吞咽异常如何使用日志记录,程序都将继续处理有意义的数据,并且吞咽不再安静。

评论


问题是页面发生了什么?

– Stilgar
2012年5月7日在7:45

我们如何知道数据有意义并且系统处于一致状态。毕竟错误是意外的,所以我们不知道会带来什么影响。

– Stilgar
2012年5月7日16:37

“自从早期失败以来,@ DeadMG(@empi)所说的快速失败是不可行的,因为Web应用程序必须在您应执行规则的错误下才能很好地工作”:人们总是可以捕获所有异常(捕获(可抛出t)),并且重定向到某些错误页面。这不可能吗?

–乔治
2012年5月19日下午5:58

#14 楼

这个问题已经有了不错的答案,可能是它们的补充:我确实喜欢代码中的断言。如果您的代码只能使用有效对象,而不能使用null,则应声明null值。

这种情况下的声明将使任何单元和/或集成测试失败,并显示准确的消息,因此您可以在生产前迅速确定问题所在。
如果这样的错误通过测试并进入生产阶段(应取消激活断言),您将收到NpEx和一个严重的错误,但这是更好,而不是一些小错误,这些小错误会更加模糊并在代码中的其他地方弹出,并且修复起来会更加昂贵。在设计/编写代码时所做的假设(在这种情况下:嘿,伙计,必须显示此对象!),这使维护更加容易。

评论


你能写点什么赞成票吗?我希望进行讨论。谢谢

–GeT
2012年5月11日20:39

我没有投反对票,我同意你的一般想法。该语言需要一些工作。您的断言定义了API的合同,如果违反了这些合同,则您会提早失败/快速失败。在应用程序的皮肤上执行此操作将使您的代码用户知道他们在代码中做错了什么。如果他们看到堆栈在您的代码凹进处下落,他们可能会认为您的代码有错误-您也可能会这样认为,直到获得可靠的repro并对其进行调试为止。

– Merlyn Morgan-Graham
2012年5月21日在9:18



#15 楼

我通常会检查null,但会抛出一个异常来“处理”意外的null,该异常会提供有关null发生位置的更多详细信息。

编辑:如果在不期望的情况下得到null一件事不对劲-程序应该死了。

#16 楼

这取决于异常的语言实现。异常使您可以采取一些措施,以防止损坏数据或在系统进入意外状态时干净地释放资源。这与错误处理(您可以预见的情况)不同。我的理念是,应尽可能减少异常处理。是噪音通过处理异常,您的方法可以做任何合理的事情吗?可以恢复吗?它可以修复或防止进一步的损坏吗?如果您只是要捕获异常,然后从方法中返回或以其他无害的方式失败,则捕获异常确实没有意义。您只会增加方法的噪声和复杂性,并且可能在设计中隐藏了故障源。在这种情况下,我会让故障冒出来并被外部循环捕获。如果您可以执行诸如修复对象或将其标记为已损坏或释放某些关键资源(例如锁定文件)或完全关闭套接字的操作,那么这是例外的一种好用法。如果您实际上希望NULL作为有效的边缘情况经常出现,那么您应该在常规逻辑流程中使用if-the-else或switch-case语句或其他方式处理此问题,而不使用异常处理。例如,以表格形式禁用的字段可以设置为NULL,这与表示保留为空白的字段的空字符串不同。这是您必须使用良好判断力和常识的领域。我从未听说过在每种情况下如何处理此问题的良好经验法则。

Java在其异常处理实现中使用“检查的异常”失败,其中必须在方法的“ throws”子句中捕获或声明方法中使用的对象可能引发的每个可能的异常。这与C ++和Python相反,您可以根据需要选择处理异常。在Java中,如果您修改方法的主体以使其包含可能引发异常的调用,则您可能会选择添加显式处理以处理可能不关心的异常,从而给代码增加噪音和复杂性,或者您必须将该异常添加到您正在修改的方法的“ throws”子句中,这不仅会增加杂音和混乱,而且会更改方法的签名。然后,无论使用哪种方法来处理您的方法现在可能会引发的新异常,或者必须将异常添加到这些方法的“ throws”子句中,从而触发代码更改的多米诺效应,您都必须转到修改代码。 “捕获或指定要求”必须是Java最令人讨厌的方面之一。 RuntimeExceptions的异常异常以及针对此设计错误的官方Java合理化简直是me脚。

最后一段与回答您的问题无关。我只是讨厌Java。

-
诺亚

评论


这篇文章很难阅读(文字墙)。您介意将其编辑为更好的形状吗?

– gna
2015年5月23日19:40