开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 4 天,点击查看活动详情
一、为什么
大多数方法和构造器对于传递给它们的参数值都会做一些校验,有如下几个原因:
1、如果不做校验,后期检测到错误的可能性比较小,也很难确定错误的来源
2、方法在执行过程中可能产生令人费解的异常
3、方法正常返回但是返回错误的结果
4、即使返回了正常的结果,也可能会导致某些对象处于被破坏的状态
二、分析
2.1 公有的和受保护的方法
说明:
1、对于公有的和受保护的方法,要用Javadoc的@throws标签在文档中说明违反参数值限制时会抛出的异常
2、文档中没有说“如果m为null,mod就抛出NullPointerException”,而是作为调用m.signum()的副产物,这样避免了分别在每个方法中给每个NullPointerException建立文档而引起的混乱
3、结合@Nullable一起使用,表示某个特殊的参数可以为null
2.2 Java支持
Java 7中增加Objects.requireNonNull,这个方法会返回其输入,因此可以在使用一个值的同时执行null检查,如下:
@Test(expected = NullPointerException.class)
public void testRequireNonNull() {
Strategy strategy = null;
Strategy stategy1 = Objects.requireNonNull(strategy, "strategy");
}
Java 9中增加Objects.checkFromIndexSize、checkFromToIndex和checkIndex,专门设计用于列表和数组索引的,如下:
@Test
public void testCheckFromIndexSize() {
// 检查[fromIndex, fromIndex + size)是否被包含于[0,length)
int[] arr = {1, 2, 3, 4, 5, 6};
int result = Objects.checkFromIndexSize(1, 5, arr.length);
Assert.assertEquals(1, result);
}
@Test
public void testCheckFromToIndex() {
// 检查[fromIndex,toIndex)是否被包含于[0,length)
int[] arr = {1, 2, 3, 4, 5, 6};
int result = Objects.checkFromToIndex(1, 3, arr.length);
Assert.assertEquals(1, result);
}
@Test
public void testCheckIndex() {
// index是否被包含于[0,length)
int[] arr = {1, 2, 3, 4, 5, 6};
int result = Objects.checkIndex(1, arr.length);
Assert.assertEquals(1, result);
}
2.3 未被导出的方法,使用断言
对于未被导出的方法(unexported method),作为包的创建者,可以控制这个方法将在哪些情况下被调用,因此应该确保只将有效的参数传递进来。因此,非公有的方法ceiop应该使用断言来检查它们的参数,具体做法如下所示:
private static void sort(long[] a, int offset, int length) {
assert a != null;
assert offset >= 0 && offset <= a.length;
assert length >= 0 && length <= a.length - offset;
...
}
三、总结
简而言之,每当编写方法或者构造器的时候,应该考虑它的参数有哪些限制。应该把这些限制写到文档中,并且在这个方法体的开头处,通过电焊工的检查来实施这些限制。养成这样的习惯非常重要。只要有效性检查有一次失败,你为必要的有效性检查所付出的努力便都可以连本带利地得到尝还了。