防御性编程:Java 指南

发布于 2024-08-05 07:27:32 字数 1293 浏览 4 评论 0原文

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

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

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

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

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

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

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

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

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

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

I’m from a .NET background and now dabbling in Java.

Currently, I’m having big problems designing an API defensively against faulty input. Let’s say I’ve got the following code (close enough):

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

However, this code can fail for two reasons:

  1. User passes a null node.
  2. User passes an invalid node, i.e. one not contained in the graph.

In .NET, I would throw an ArgumentNullException (rather than a NullReferenceException!) or an ArgumentException respectively, passing the name of the offending argument (node) as a string argument.

Java doesn’t seem to have equivalent exceptions. I realize that I could be more specific and just throw whatever exception comes closest to describing the situation, or even writing my own exception class for the specific situation.

Is this the best practice? Or are there general-purpose classes similar to ArgumentException in .NET?

Does it even make sense to check against null in this case? The code will fail anyway and the exception’s stack trace will contain the above method call. Checking against null seems redundant and excessive. Granted, the stack trace will be slightly cleaner (since its target is the above method, rather than an internal check in the HashMap implementation of the JRE). But this must be offset against the cost of an additional if statement, which, furthermore, should never occur anyway – after all, passing null to the above method isn’t an expected situation, it’s a rather stupid bug. Expecting it is downright paranoid – and it will fail with the same exception even if I don’t check for it.

[As has been pointed out in the comments, HashMap.put actually allows null values for the key. So a check against null wouldn’t necessarily be redundant here.]

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

不羁少年 2024-08-12 07:27:32

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

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

The standard Java exception is IllegalArgumentException. Some will throw NullPointerException if the argument is null, but for me NPE has that "someone screwed up" connotation, and you don't want clients of your API to think you don't know what you're doing.

For public APIs, check the arguments and fail early and cleanly. The time/cost barely matters.

暗喜 2024-08-12 07:27:32

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

首先,我假设您知道 RuntimeException(未检查)和普通 Exception(检查)之间的区别,如果不知道,请参阅 这个问题和答案。如果您编写自己的异常,则可以强制捕获它,而 NullPointerExceptionIllegalArgumentException 都是 RuntimeException,在某些圈子里是不受欢迎的。

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

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

正如其他人所说 IllegalArgumentException 是一个选项,但我认为 NullPointerException 更具沟通性。

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

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

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

Different groups have different standards.

Firstly, I assume you know the difference between RuntimeExceptions (unchecked) and normal Exceptions (checked), if not then see this question and the answers. If you write your own exception you can force it to be caught, whereas both NullPointerException and IllegalArgumentException are RuntimeExceptions which are frowned on in some circles.

Secondly, as with you, groups I've worked with but don't actively use asserts, but if your team (or consumer of the API) has decided it will use asserts, then assert sounds like precisely the correct mechanism.

If I was you I would use NullPointerException. The reason for this is precedent. Take an example Java API from Sun, for example java.util.TreeSet. This uses NPEs for precisely this sort of situation, and while it does look like your code just used a null, it is entirely appropriate.

As others have said IllegalArgumentException is an option, but I think NullPointerException is more communicative.

If this API is designed to be used by outside companies/teams I would stick with NullPointerException, but make sure it is declared in the javadoc. If it is for internal use then you might decide that adding your own Exception heirarchy is worthwhile, but personally I find that APIs which add huge exception heirarchies, which are only going to be printStackTrace()d or logged are just a waste of effort.

At the end of the day the main thing is that your code communicates clearly. A local exception heirarchy is like local jargon - it adds information for insiders but can baffle outsiders.

As regards checking against null I would argue it does make sense. Firstly, it allows you to add a message about what was null (ie node or tokens) when you construct the exception which would be helpful. Secondly, in future you might use a Map implementation which allows null, and then you would lose the error check. The cost is almost nothing, so unless a profiler says it is an inner loop problem I wouldn't worry about it.

↙厌世 2024-08-12 07:27:32

在 Java 中,您通常会抛出 IllegalArgumentException

In Java you would normally throw an IllegalArgumentException

甜妞爱困 2024-08-12 07:27:32

如果您想要有关如何编写良好 Java 代码的指南,我强烈推荐这本书 Effective Java约书亚·布洛赫 (Joshua Bloch) 着。

If you want a guide about how to write good Java code, I can highly recommend the book Effective Java by Joshua Bloch.

若有似无的小暗淡 2024-08-12 07:27:32

听起来这可能适合 assert

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

It sounds like this might be an appropriate use for an assert:

public void setTokens(Node node, int newTokens) {
    assert node != null;
    tokens.put(node, newTokens);
}
下雨或天晴 2024-08-12 07:27:32

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

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

Your approach depends entirely on what contract your function offers to callers - is it a precondition that node is not null?

If it is then you should throw an exception if node is null, since it is a contract violation. If it isnt then your function should silently handle the null Node and respond appropriately.

岁月打碎记忆 2024-08-12 07:27:32

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

在该过程中的某个时刻,调用者可以在调用您的方法之前采取措施来验证节点。如果您认识调用者并且知道这些节点始终经过验证,那么我认为可以假设您会获得良好的数据。本质上责任在于呼叫者。

但是,例如,如果您提供分布式第三方库,那么您需要验证节点是否为 null 等...

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

I think a lot depends on the contract of the method and how well the caller is known.

At some point in the process the caller could take action to validate the node before calling your method. If you know the caller and know that these nodes are always validated then i think it is ok to assume you'll get good data. Essentially responsibility is on the caller.

However if you are, for example, providing a third party library that is distributed then you need to validate the node for nulls, etcs...

An illegalArugementException is the java standard but is also a RunTimeException. So if you want to force the caller to handle the exception then you need to provided a check exception, probably a custom one you create.

Bonjour°[大白 2024-08-12 07:27:32

就我个人而言,我希望 NullPointerExceptions 仅意外发生,因此必须使用其他内容来指示传递了非法参数值。 IllegalArgumentException 适合于此。

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

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

(并且,始终为您的例外情况提供解释性文字,您会在某个悲伤的日子欣赏它们)

Personally I'd like NullPointerExceptions to ONLY happen by accident, so something else must be used to indicate that an illegal argument value was passed. IllegalArgumentException is fine for this.

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

This should be sufficient to both those reading the code, but also the poor soul who gets a support call at 3 in the morning.

(and, ALWAYS provide an explanatory text for your exceptions, you will appreciate them some sad day)

梦忆晨望 2024-08-12 07:27:32

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

like the other : java.lang.IllegalArgumentException.
About checking null Node, what about checking bad input at the Node creation ?

浅浅淡淡 2024-08-12 07:27:32

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

void method(String s) 

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

让我睡个好觉。

其他人会不同意。

I don't have to please anybody, so what I do now as canonical code is

void method(String s) 

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

which gets me a lot of sleep.

Others will disagree.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文