8

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

目前,我在设计一个针对错误输入的防御性 API 时遇到了很大的问题。假设我有以下代码(足够接近):

public void setTokens(Node node, int newTokens) {
    tokens.put(node, newTokens);
}

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

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

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

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

这是最佳做法吗?ArgumentException或者是否有类似于.NET 中的通用类?

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

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

4

10 回答 10

12

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

对于公共 API,请检查参数并尽早彻底地失败。时间/成本几乎无关紧要。

于 2009-09-18T12:49:07.450 回答
7

在 Java 中,您通常会抛出 IllegalArgumentException

于 2009-09-18T12:46:37.380 回答
7

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

RuntimeException首先,我假设您知道s(未选中)和 normal s(选中)之间的区别Exception,如果不知道,请查看此问题和答案。如果您编写自己的异常,则可以强制捕获它,而两者NullPointerException都是IllegalArgumentExceptionRuntimeExceptions,在某些圈子中不受欢迎。

其次,和你一样,我曾与之合作但不积极使用断言的小组,但如果你的团队(或 API 的消费者)决定使用断言,那么断言听起来正是正确的机制。

如果我是你,我会使用NullPointerException. 其原因是先例。以 Sun 的 Java API 为例,例如java.util.TreeSet。这正是在这种情况下使用 NPE,虽然看起来您的代码确实使用了 null,但它是完全合适的。

正如其他人所说IllegalArgumentException的是一种选择,但我认为 NullPointerException 更具交流性。

如果此 API 旨在供外部公司/团队使用,我会坚持使用NullPointerException,但请确保在 javadoc 中声明它。如果它是供内部使用的,那么您可能会认为添加自己的异常层次结构是值得的,但我个人发现添加大量异常层次结构的 API 只会被printStackTrace()d 或记录下来只是浪费精力。

归根结底,最重要的是您的代码可以清晰地交流。本地异常层次结构就像本地行话 - 它为内部人员添加信息,但会使外部人员感到困惑。

至于检查 null 我认为它确实是有道理的。首先,它允许您在构造异常时添加关于什么为空(即节点或令牌)的消息,这将很有帮助。其次,将来您可能会使用Map允许的实现null,然后您将丢失错误检查。成本几乎为零,所以除非分析器说这是一个内部循环问题,否则我不会担心。

于 2009-09-18T13:28:19.013 回答
2

您的方法完全取决于您的函数为调用者提供的合同 - 是否节点不为空的前提条件?

如果是,那么如果 node 为空,则应该抛出异常,因为它违反了合同。如果不是,那么您的函数应该静默处理空节点并做出适当的响应。

于 2009-09-18T12:48:41.337 回答
2

如果你想了解如何编写好的 Java 代码,我强烈推荐Joshua Bloch的《 Effective Java 》一书。

于 2009-09-18T12:49:11.897 回答
2

听起来这可能是assert的适当用途:

public void setTokens(Node node, int newTokens) {
    assert node != null;
    tokens.put(node, newTokens);
}
于 2009-09-18T12:52:59.707 回答
1

我认为很大程度上取决于方法的合同以及调用者的了解程度。

在过程中的某个时刻,调用者可以在调用您的方法之前采取行动来验证节点。如果您认识调用者并且知道这些节点总是经过验证,那么我认为可以假设您将获得良好的数据。本质上,责任在调用者身上。

但是,例如,如果您要提供一个分布式的第三方库,那么您需要验证节点是否存在空值等...

非法ArumentException 是Java 标准,但也是RunTimeException。因此,如果您想强制调用者处理异常,那么您需要提供一个检查异常,可能是您创建的自定义异常。

于 2009-09-18T13:34:22.127 回答
1

就我个人而言,我希望 NullPointerExceptions 只是偶然发生,所以必须使用其他东西来指示传递了一个非法的参数值。IllegalArgumentException 对此很好。

if (arg1 == null) {
 throw new IllegalArgumentException("arg1 == null");
}

这对于阅读代码的人来说应该足够了,对于凌晨 3 点接到支持电话的可怜人来说也足够了。

(并且,总是为您的例外情况提供解释性文字,您会在悲伤的一天感谢他们)

于 2009-09-18T14:40:16.417 回答
0

像另一个:java.lang.IllegalArgumentException。关于检查空节点,在创建节点时检查错误输入怎么样?

于 2009-09-18T12:51:25.670 回答
0

我不必取悦任何人,所以我现在作为规范代码所做的是

void method(String s) 

if((s != null) && (s instanceof String) && (s.length() > 0x0000))
{

这让我睡了很多觉。

其他人会不同意。

于 2009-10-12T04:18:54.237 回答