防御性编程:Java指南

Kon*_*lph 8 java defensive-programming exception

我来自.NET背景,现在涉足Java.

目前,我在设计防御输入错误的API方面遇到了大问题.假设我有以下代码(足够接近):

public void setTokens(Node node, int newTokens) {
    tokens.put(node, newTokens);
}
Run Code Online (Sandbox Code Playgroud)

但是,此代码可能会失败有两个原因:

  1. 用户传递null节点.
  2. 用户传递无效节点,即未包含在图中的节点.

在.NET中,我会抛出一个ArgumentNullException(而不是一个NullReferenceException!)或ArgumentException分别传递有问题的参数(node)的名称作为string参数.

Java似乎没有等效的例外.我意识到我可以更具体,只是抛出最接近描述情况的异常,或者甚至为特定情况编写我自己的异常类.

这是最好的做法吗?或者是否存在类似于ArgumentException.NET 的通用类?

null在这种情况下检查是否有意义?无论如何代码都会失败,异常的堆栈跟踪将包含上面的方法调用.检查null似乎多余和过度.当然,堆栈跟踪会稍微清晰一些(因为它的目标是上面的方法,而不是HashMapJRE实现中的内部检查).但这必须抵消额外if声明的成本,此外,永远不应该发生 - 毕竟,传递null给上述方法不是预期的情况,这是一个相当愚蠢的错误.期待它是彻头彻尾的偏执 - 即使我不检查它也会失败,同样的例外.

[正如评论中指出的那样,HashMap.put实际上允许null键的值.所以这里的检查null不一定是多余的.]

小智 12

标准的Java异常是IllegalArgumentException.NullPointerException如果参数为null,有些人会抛出,但对我来说NPE有"有人搞砸"的含义,你不希望你的API的客户认为你不知道你在做什么.

对于公共API,请检查参数并尽早和干净地失败.时间/成本几乎不重要.


For*_*ner 7

在Java中,您通常会抛出IllegalArgumentException

  • BTW.我们使用Jakarta Commons-Lang进行合同样式检查.例如Validate.notNull(node)或Validate.isTrue(node.isEmpty())这些为你抛出IllegalArgumentExceptions (2认同)

Nic*_*cue 7

不同的群体有不同的标准.

首先,我假设您知道RuntimeExceptions(未检查)和正常Exceptions(已检查)之间的区别,如果没有,则查看此问题和答案.如果你写你自己的异常,你可以强制它被抓获,而这两个NullPointerExceptionIllegalArgumentException是它们在某些圈子里不赞成RuntimeExceptions.

其次,和你一样,我曾经合作但不主动使用断言的小组,但如果你的团队(或API的消费者)已经决定使用断言,那么断言就像正确的机制一样.

如果我是你,我会用NullPointerException.其原因是先例.从Sun获取示例Java API,例如java.util.TreeSet.这使用NPE来处理这种情况,虽然它看起来像你的代码只是使用了null,但这是完全合适的.

正如其他人所说的那样IllegalArgumentException是一种选择,但我认为NullPointerException更具沟通性.

如果这个API设计为外部公司/团队使用NullPointerException,我会坚持,但要确保它在javadoc中声明.如果它是供内部使用的话,你可能会认为添加你自己的Exception heirarchy是值得的,但我个人觉得那些添加了大量异常heirarchies的API只会printStackTrace()浪费精力.

在一天结束时,主要的是您的代码清晰地沟通.本地例外的heirarchy就像当地的行话一样 - 它为内部人员添加了信息,但可能会让局外人感到困惑.

关于检查null,我认为它确实有意义.首先,它允许您在构造有用的异常时添加有关null(即节点或标记)的消息.其次,将来您可能会使用Map允许的实现null,然后您将丢失错误检查.成本几乎为零,所以除非剖析器说这是一个内循环问题,否则我不担心.