第49条:检查参数的有效性
几乎所有公有方法和构造器,都要对传入参数做合法性校验,非法参数应该尽早失败,而不是等到执行中途才崩溃。
这也是新手,在第一次接触真实项目时经常会犯得错误,毕竟当写个人项目时你可以随意一点,但是在企业复杂庞大的项目中,方法的参数校验是十分重要的,及时的抛出异常。
- 及时校验方法参数
若传递了一个无效参数,在方法开头就对参数进行了校验,那么可以很快的发现异常,不要等到在执行过程中失败,最糟糕的情况是无效参数正常执行返回错误的结果。 - 对于共有方法必须进行校验,私有的可以不校验
公有方法是对外契约,别人乱传参数你必须拦截;私有方法是自己内部调用,可控,可以省略校验。 - 校验失败统一抛出标准异常
不要抛自定义乱七八糟异常,遵循 Java 约定,本书中指的底层入参合法性校验、编程错误。我们开发web项目出现的业务异常,使用统一的自定义异常即可。
当成也有例外,对于一些不实际的校验,是可以不校验的,比如在sort(list)前校验每个元素的可以排序的,这没有什么意义。
校验也不是越多越好,要在方法设计考虑通用性,尽量简洁,只添加必要的检查。
例如
参数校验
java
public void setAge(int age) {
// 先校验参数,再执行业务逻辑
if (age < 0 || age > 150) {
throw new IllegalArgumentException("年龄不合法:" + age);
}
// 业务逻辑
}
空指针校验
java
public void print(String msg) {
Objects.requireNonNull(msg, "msg不能为null");
}
这是书中推荐的,真实项目很少用。业务开发一般使用自定义的异常。
注意点
- 先赋值成员变量,再校验参数 ❌
错误构造器先把非法参数赋值给字段,对象已经非法了。正确:先校验 → 再赋值 - 空字符串判断
字符串要同时判 null 和空:if(str == null || str.isBlank())。
一般使用
java
StringUtils.isEmpty(str);
这里也衍生出其他问题,比如List不为空,但是list中没有元素的校验。
有一个小技巧,如果是字符串使用StringUtils.isEmpty(str)或StringUtils.hasText(str),否则使用ObjectUtils.isEmpty(str),内部对于各种容器等都进行的判断。
源码:
java
public static boolean isEmpty(@Nullable Object obj) {
if (obj == null) {
return true;
} else if (obj instanceof Optional) {
Optional<?> optional = (Optional)obj;
return optional.isEmpty();
} else if (obj instanceof CharSequence) {
CharSequence charSequence = (CharSequence)obj;
return charSequence.isEmpty();
} else if (obj.getClass().isArray()) {
return Array.getLength(obj) == 0;
} else if (obj instanceof Collection) {
Collection<?> collection = (Collection)obj;
return collection.isEmpty();
} else if (obj instanceof Map) {
Map<?, ?> map = (Map)obj;
return map.isEmpty();
} else {
return false;
}
}
- 校验逻辑写在工具方法里,公有方法不校验对
外接口必须自己校验,不能依赖内部工具。 - 返回错误码,不抛异常违反本条设计哲学:非法参数是编程错误,不是业务异常,应该直接崩溃失败快速,不能吞错。
- 个人建议在调用外部接口获取数据时,对返回结果进行空校验,同时在使用Stream前对于数据进行空校验。
总结
更重要的是在日常养成好的习惯,在写方法时就考虑好参数的限制。不要当出现问题时,才明白参数校验的重要性。