Google Guava之Preconditions
文中所述Guava版本基于29.0-jre
,文中涉及到的代码完整示例请移步Github查看。
如何编写参数条件判定语句
在我们编写一些方法的时候,都需要对方法传入的参数进行一些条件限定,比如
/**
* 以自然顺序比较两个字符串并返回较大的字符串
*/
public String compare(String firstString, String secondString) {
if (firstString == null) {
throw new NullPointerException("Argument 'firstString' expected non-null");
}
if (secondString == null) {
throw new NullPointerException("Argument 'secondString' expected non-null");
}
if (firstString.compareTo(secondString) >= 0) {
return firstString;
}
return secondString;
}
这样当条件不满足的时候,就会打印出对应的异常,能够帮助我们快速定位错误原因,但是如果每个参数都需要编写类似这样if判断的语句,就让代码很臃肿。Guava提供一个工具类,帮助我们方便的值进行条件判定。
/**
* 以自然顺序比较两个字符串并返回较大的字符串
*/
public String compareSimple(String firstString, String secondString) {
Preconditions.checkNotNull(firstString, "Argument 'firstString' expected non-null");
Preconditions.checkNotNull(secondString, "Argument 'secondString' expected non-null");
if (firstString.compareTo(secondString) >= 0) {
return firstString;
}
return secondString;
}
这样将原本需要if判断的代码语句简化为单条Preconditions
语句,其实Preconditions
也就是替我们做了if的判断,checkNotNull
的源代码如下
@CanIgnoreReturnValue
public static <T> T checkNotNull(T reference, @Nullable Object errorMessage) {
if (reference == null) {
throw new NullPointerException(String.valueOf(errorMessage));
} else {
return reference;
}
}
在java.util
包中也有实现类似功能的工具类,就是java.util.Objects
,我们来看下java.util.Objects
的用法
Objects.requireNonNull(secondString, "Argument 'secondString' expected non-null");
既然已经有了类似的方法,为什么Guava还要重复造轮子呢?原因在于java.util.Objects
提供的条件判定方案太少,另外Guava提供更方便的异常信息打印(Preconditions
提供可变参数列表,可供我们打印多余信息,比如可以在条件不满足时打印上下文变量值,下文可看到在条件不满足时打印出方法所有参数值)。
但是我仍认为Preconditions
提供的功能不是很完美,首先Preconditions.checkNotNull(firstString)
只有单个参数时,如果firstString为null,则打印的时没有异常信息的空指针异常,这导致我们想要获取异常详细信息都需要自己来编写异常提示信息
java.lang.NullPointerException
at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:878)
我希望Preconditions提供的条件判定语句在只提供单个参数时,能够自己组织一条有意义的异常提示信息。
另外如果对于某个参数需要多重条件判断,比如我们判定参数firstString既要不为null,也要长度大于5,这时候就需要写两条Preconditions语句
/**
* 以自然顺序比较两个字符串并返回较大的字符串
*/
public String compareSimple(String firstString, String secondString) {
Preconditions.checkNotNull(firstString, "Argument 'firstString' expected non-null", firstString, secondString);
Preconditions.checkArgument(firstString.length() > 5, "Argument 'firstString' expected length > 5", firstString, secondString);
Preconditions.checkNotNull(secondString, "Argument 'secondString' expected non-null");
if (firstString.compareTo(secondString) >= 0) {
return firstString;
}
return secondString;
}
// caller
compareSimple("test", "test");
// output
java.lang.IllegalArgumentException: Argument 'firstString' expected length > 5 [test, test]
我们也可以把两条Preconditions语句合为单条
Preconditions.checkArgument(firstString != null && firstString.length() > 5, "Argument 'firstString' expected non-null and length > 5", firstString, secondString);
但是这种情况下当参数为null或者长度小于5时,打印相同的信息,不利于我们对失败原因的判定,建议Guava在以后能够提供满足多条件判定的Preconditions语句。
Preconditions提供的方法
方法签名 | 描述 | 失败时抛出的异常 |
---|---|---|
checkArgument(boolean) |
检查条件判定的布尔值是否是true,用来判定方法的参数。 | IllegalArgumentException |
checkNotNull(T) |
检查值是否为null,不为null则返回该值。 | NullPointerException |
checkState(boolean) |
检查对象的某些状态,不依赖于方法参数。例如,一个Iterator可能会使用它来检查在调用remove之前是否已调用next。 | IllegalStateException |
checkElementIndex(int index, int size) |
检查索引是否为列表,字符串或数组的有效元素索引。元素索引可能是[0, size),不需要把列表,字符串或数组作为参数传递,直接传递它的size。返该方法返回值为index。 | IndexOutOfBoundsException |
checkPositionIndex(int index, int size) |
检查索引是否为列表,字符串或数组的有效元素索引。元素索引可能是[0, size],不需要把列表,字符串或数组作为参数传递,直接传递它的size。返该方法返回值为index。 | IndexOutOfBoundsException |
checkPositionIndexes(int start, int end, int size) |
检查[start, end)是否是列表,字符串或数组的有效子范围。返回代码自组织的错误信息。 | IndexOutOfBoundsException |