代码之家  ›  专栏  ›  技术社区  ›  Konrad Rudolph

防御性编程:Java指南

  •  8
  • Konrad Rudolph  · 技术社区  · 15 年前

    我来自.NET后台,现在涉足Java。

    目前,我在设计一个API以抵御错误输入方面遇到了很大的问题。假设i_已经得到以下代码(足够接近):

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

    但是,此代码失败的原因有两个:

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

    在.NET中,我将 ArgumentNullException (而不是 NullReferenceException !)或 ArgumentException 分别传递违规论点的名称( node 作为一个 string 争论。

    Java似乎没有等价的例外。我意识到我可以更具体一些,然后抛出任何最接近描述情况的异常,或者甚至为特定情况编写自己的异常类。

    这是最佳实践吗?或者有类似于 参数异常 在.NET中?

    检查是否有意义 无效的 在这种情况下?代码无论如何都会失败,异常的堆栈跟踪将包含上述方法调用。核对 无效的 似乎是多余和过度的。如果允许,堆栈跟踪将 轻微地 更干净(因为它的目标是上述方法,而不是 HashMap JRE的实现)。但这必须抵消额外费用 if 声明,而且,应该 从未 无论如何都会发生,通过 无效的 对于上述方法来说,这不是一个预期的情况,而是一个相当愚蠢的错误。期望它是完全偏执的,即使我不检查它,它也会以同样的例外而失败。

    [正如评论中指出的, HashMap.put 事实上允许 无效的 键的值。所以一张支票 无效的 这里不一定是多余的。]

    10 回复  |  直到 15 年前
        1
  •  7
  •   Community CDub    8 年前

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

    首先,我想你知道 RuntimeException S(未选中)和Normal Exception S(选中),如果没有,请参见 this question and the answers . 如果你写下你自己的例外,你可以强迫它被抓住,但是两者都是 NullPointerException IllegalArgumentException 是一些循环中不受欢迎的RuntimeExceptions。

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

    如果我是你,我会用 空指针异常 . 原因是先例。以Sun公司的Java API为例 java.util.TreeSet . 在这种情况下,它使用NPE,虽然看起来您的代码只是使用了一个空值,但这是完全合适的。

    正如其他人所说 非法数据异常 是一种选择,但我认为nullpointerException更具交际性。

    如果这个API是为外部公司/团队设计的,我会坚持使用它 空指针异常 但要确保它在javadoc中声明。如果它是内部使用的,那么您可能会决定添加自己的异常继承权是值得的,但是我个人发现添加了大量异常继承权的API,它们只会 printStackTrace() 或者记录只是浪费精力。

    在一天结束的时候,主要的事情是你的代码可以清晰地通信。当地的例外继承制度就像当地的行话一样——它为内部人士添加信息,但也会迷惑外部人士。

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

        2
  •  12
  •   Ken    15 年前

    标准的Java异常是 IllegalArgumentException . 有些会投掷 NullPointerException 如果这个论点是空的,但是对我来说,NPE有“某人搞砸了”的含义,并且你不希望API的客户认为你不知道自己在做什么。

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

        3
  •  7
  •   Fortyrunner    15 年前

    在Java中,通常会抛出一个非法的异常事件。

        4
  •  2
  •   Jesper    15 年前

    如果你想要一个关于如何编写好Java代码的指南,我可以高度推荐这本书。 Effective Java 乔舒亚·布洛克。

        5
  •  2
  •   Greg Hewgill    15 年前

    听起来这可能是 assert :

    public void setTokens(Node node, int newTokens) {
        assert node != null;
        tokens.put(node, newTokens);
    }
    
        6
  •  2
  •   Jim Ferrans    15 年前

    您的方法完全取决于您的函数向调用者提供的契约——节点不为空是一个先决条件吗?

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

        7
  •  1
  •   richs    15 年前

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

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

    但是,如果您提供了一个分布式的第三方库,那么您需要验证节点是否为空,ETC…

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

        8
  •  1
  •   Thorbjørn Ravn Andersen    15 年前

    我个人希望nullpointerExceptions只在意外情况下发生,因此必须使用其他东西来指示传递了非法参数值。IllegalArgumentException可以。

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

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

    (而且,总是为你的例外情况提供一个解释性的文本,你会在悲伤的一天感激他们)

        9
  •  0
  •   Antoine Claval    15 年前

    与另一个类似:java.lang.IllegalArgumentException。 关于检查空节点,那么在节点创建时检查错误的输入呢?

        10
  •  0
  •   Nicholas Jordan    15 年前

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

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

    这让我睡得很香。

    其他人会不同意。