程序员你如何检查参数的合法性?

image.png

作为程序员的你,代码中最多的就是各种方法了,你是如何对参数进行校验的呢?

背景

大部分的方法和构造函数对传入的参数值有一些限制,比如:常见的索引值必须是非负数,对象引用不能为空。

你应该使用清晰的文档来标注所有的这些限制,然后在方法体开始的地方强制他们检查。

应该在错误发生的时候尽快的检查出来,这是基本原则。

如果你不这么做,当错误发生的时候,错误将不会被检测出来,这让定位错误的源头变得更困难。

如果一个非法参数传递到一个方法中,在方法执行前进行了参数检查。它将会快速失败,并给出清晰的异常信息。

如果方法没有检查参数,下面这些事情会发生。

程度 说明
糟糕 方法会在执行过程中失败然后抛出一个不明确的异常;
更糟糕 方法会正常返回,但是悄悄的计算了一个错误的值。
最糟糕 方法正常返回,但是一些对象处在一个不正确的状态,未来一个不确定的时间点在某些无关联的点会造成一个错误。

一句话总结:参数不校验会导致原子性失败

推荐做法

对公共和保护方法,使用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,这是排序方法应该做的。

所以:这里有一个小店,在开始的时候检查列表中的元素应该是可以互相比较的,注意:修改合法性检查会丧失原子失败

偶尔,一个计算执行了一个需要的合法性检查,但是当执行检查失败的时候,抛出了一个错误的异常。换句话说,计算常常会抛出参数合法性检查的异常,并不会匹配方法在文档中申明的异常。这种场景下,你应该使用异常翻译成语。 转换自然异常为正确的异常。

这个原则并不是说武断的限制参数是一件好事,而是说:你应该设计通用实际的方法。
假设你的方法接受所有的参数组合而可以做一些合理事情,你的参数限制越少越好,然而,一些限制本质上在抽象类中已经被实现了。

小结

如果看完之后你只能记住一句话:每次你写一个方法或者一个构造函数,你应该思考参数的限制是否存在,你应该把限制写在文档中,并在方法体的开始部分确保进行了检查

养成这个习惯很重要,适当的工作会在第一次合法性检查失败的时候回馈你。

检查参数的合法性.png

原创不易,关注诚可贵,转发价更高!转载请注明出处,让我们互通有无,共同进步,欢迎沟通交流。
我会持续分享Java软件编程知识和程序员发展职业之路,欢迎关注!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352