作为程序员的你,代码中最多的就是各种办法了,你是如何对参数进行校验的呢?
背景
大部分的办法和构造函数对传入的参数值有一些限度,比方:常见的索引值必须是非正数,对象援用不能为空。
你应该应用清晰的文档来标注所有的这些限度,而后在办法体开始的中央强制他们查看。
应该在谬误产生的时候尽快的查看进去,这是根本准则。
如果你不这么做,当谬误产生的时候,谬误将不会被检测进去,这让定位谬误的源头变得更艰难。
如果一个非法参数传递到一个办法中,在办法执行前进行了参数查看。它将会疾速失败,并给出清晰的异样信息。
如果办法没有查看参数,上面这些事件会产生。
水平 | 阐明 |
---|---|
蹩脚 | 办法会在执行过程中失败而后抛出一个不明确的异样; |
更蹩脚 | 办法会失常返回,然而悄悄的计算了一个谬误的值。 |
最蹩脚 | 办法失常返回,然而一些对象处在一个不正确的状态,将来一个不确定的工夫点在某些无关联的点会造成一个谬误。 |
一句话总结:参数不校验会导致 原子性失败。
举荐做法
对公共和爱护办法,应用 java 文档的 @throws 标签来标注参数值不非法将抛出的异样。
常见的参数校验的异样类型如下:
异样名称 | 阐明 |
---|---|
IllegalArgumentException | 非法参数 |
IndexOutOfBoundsException | 数组越界 |
NullPointerException | 空指针 |
只有你曾经曾经在文档中标注了办法参数的限度和违反限度会抛出的异样,限度将是一个简略的事件,上面是一个典型的例子。
/**
*@param m 必须是正整数
*@throws ArithmeticException 如果 m <=0
**/
public BigInteger mod(BigInteger m){if(m<=0){throw new ArithmeticException("modulus <=0:"+ m);
}
//todo 其它代码
}
留神:
文档正文并没有说,如果 m 是空,mod 将抛出 NullPointException,只管这个办法的确会这样。调用 m.signum()的时候这个异样被标注在类级别 BigInteger 的文档正文上,类级别的正文实用于所有的公共办法的参数,这是一个防止在每个办法独自的文档化标注 NullPointException 这种凌乱的好办法。
兴许能够联合 @Nullable 或者相似的注解来指明非凡参数能够为空,然而这个实际并不是规范的,并且有很多注解能够用来达到这个目标。
Objects 实用类
Objects.requireNonNull 办法 ,在 Java7 中增加的,十分的灵便和不便,所以没有理由手动的执行空指针查看。 你也能够指定异样的详细信息,这个办法返回本人的输出,所以你能够在应用该值的时候执行一个空指针查看。
// 一行代码应用 java 的空指针查看
this.strategy = Objects.requireNonNull(strategy,"strategy")
如果你能够疏忽返回值,你也能够依据你的须要应用 Objects.requireNonNull 作为独立的空指针查看。
在 Java9 中,一个范畴查看的办法被增加到了 java.util.Objects 中,蕴含了 3 个办法:
办法 | 阐明 |
---|---|
checkFromIndexSize | |
checkFromToIndex | |
checkIndex |
这 3 个办法没有空指针查看办法灵便,它无奈让你指定本人的异样详细信息,它被设计用在 List 和 Array 的索引查看上。
它也无奈解决闭区间,然而只有你须要,这就是一个小便当。
Java 断言
对一个不凋谢的办法,你作为包的作者,管制着办法的调用情况,你必须保障只有非法的参数值传递进去了。所以,对非公开的办法,你能够应用断言来进行参数查看,如下所示:
// 公有帮忙排序函数
private static void sort(long a[] , int offset, int length){
assert a != null ;
// 更多代码
}
实质上来讲,断言申明条件肯定是 true , 疏忽客户端如何应用对应的包。
跟个别的合法性检查不同,断言失败的时候抛出 AssertError;
跟个别的合法性检查不同,除非你启用他们否则断言对你没有任何影响和耗费。
在 java 命令行启用指令:
-ea
或者
-enableassertions
更多断言的信息,查看 java 手册的 Asserts;
查看参数的合法性十分重要,即便你的办法中没有用到,然而存储起来了,前面会用到。
举个例子:动态工厂办法:输出一个 int 数组,返回一个 array 的 list 视图,如果客户端传入 null, 这个办法会抛出 NPE, 因为办法会有一个间接查看,调用了 Objects.requireNonNull。
如果疏忽查看,办法会返回一个援用新创建的 List 的实例;
而客户端尝试应用的时候回抛出 NPE; 这个时候,原始的 List 实例很难决定,很大可能会简单到变成一个调试工作。
构造函数代表了一个非凡例子的准则:你应该查看行将存储稍后会用到的参数的合法性。
查看结构函数参数的合法性十分重要,它能够避免结构一个违反类的不变性的对象。
异常情况
在执行办法计算之前,你应该查看办法参数。这个规定也有异常情况。
一个重要的异常情况是:合法性检查代价十分高并且重要,并且查看是在执行计算的过程中执行的。
举个例子:有一个办法对一个对象 list 排序,比方 Collectios.sort(list), 所有的 list 中的对象必须是可相互比拟的。在解决 list 比拟的时候, 每个对象将会跟其它的对象进行比拟,
如果对象不能相互比拟,其中一个或多个比拟会抛出 ClassCastException,这是排序办法应该做的。
所以:这里有一个小店,在开始的时候查看列表中的元素应该是能够相互比拟的,留神:批改合法性检查会丢失 原子失败。
偶然,一个计算执行了一个须要的合法性检查,然而当执行查看失败的时候,抛出了一个谬误的异样。换句话说,计算经常会抛出参数合法性检查的异样,并不会匹配办法在文档中申明的异样。这种场景下,你应该应用异样翻译成语。转换天然异样为正确的异样。
这个准则并不是说果断的限度参数是一件坏事, 而是说:你应该设计通用理论的办法。
假如你的办法承受所有的参数组合而能够做一些正当事件,你的参数限度越少越好,然而,一些限度实质上在抽象类中曾经被实现了。
小结
如果看完之后你只能记住一句话:每次你写一个办法或者一个构造函数,你应该思考参数的限度是否存在,你应该把限度写在文档中,并在办法体的开始局部确保进行了查看。
养成这个习惯很重要,适当的工作会在第一次合法性检查失败的时候回馈你。
原创不易,关注诚可贵,转发价更高!转载请注明出处,让咱们互通有无,共同进步,欢送沟通交流。