在日常开发中,我们经常会对方法的输入参数做一些数据格式上的验证,以便保证方法能够按照正常流程执行下去。对于可预知的一些数据上的错误,我们一定要做事前检测和判断,来避免程序流程出错,而不是完全通过错误处理来保证流程正确执行,毕竟错误处理是比较消耗资源的方式。在平常情况下我们对参数的判断都需要自己来逐个写方法判断,代码量不少并且复用性不高,如下所示:
public class GuavaTest {
@Test
public void Preconditions() throws Exception {
getPerson(28, "zhangsan");
getPerson(-28, "zhangsan");
getPerson(28, "");
getPerson(28, null);
}
public static void getPerson(int age, String name) throws Exception {
if (age > 0 && StringUtils.isNoneBlank(name)) {
System.out.println("a person age:" + age + ",neme:" + name);
} else {
System.out.println("参数输入有误!");
}
}
}
说明:参数验证,我们每次都要添加if语句来做判断, 重复的工作会做好多次。getPerson方法只有2个参数,验证规则也不是很复杂,如果参数过度,验证规则复杂后,上面代码的可读性都会很差的,复用性就更谈不上了。
Guava类库中提供了一个作参数检查的工具类--Preconditions类, 该类可以大大地简化我们代码中对于参数的预判断和处理,让我们对方法输入参数的验证实现起来更加简单优雅,下面我们看看Preconditions类的使用实例:
public class GuavaTest {
@Test
public void Preconditions() throws Exception {
getPersonByPrecondition(28, "zhangsan");
try {
getPersonByPrecondition(-28, "zhangsan");
} catch (Exception e) {
System.out.println(e.getMessage());
}
try {
getPersonByPrecondition(28, "");
} catch (Exception e) {
System.out.println(e.getMessage());
}
try {
getPersonByPrecondition(28, null);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
public static void getPersonByPrecondition(int age, String name) throws Exception {
Preconditions.checkNotNull(name, "neme为null");
Preconditions.checkArgument(name.length() > 0, "neme为\'\'");
Preconditions.checkArgument(age > 0, "age 必须大于0");
System.out.println("a person age:" + age + ",neme:" + name);
}
}
运行结果:
a person age:28,neme:zhangsan
age 必须大于0
neme为''
neme为null
Preconditions里面的方法:
1 .checkArgument
public static void checkArgument(boolean expression) { if (!expression) { throw new IllegalArgumentException(); } }
public static void checkArgument(boolean expression, Object errorMessage) { if (!expression) { throw new IllegalArgumentException(String.valueOf(errorMessage)); } }
功能描述:检查expression是否为真。 用作方法中检查参数失败时抛出IllegalArgumentException异常。
2.checkNotNull
public static <T> T checkNotNull(T reference) { if (reference == null) { throw new NullPointerException(); } return reference; }
public static <T> T checkNotNull(T reference, @Nullable Object errorMessage) { if (reference == null) { throw new NullPointerException(String.valueOf(errorMessage)); } return reference; }
功能描述:检查reference不为null, 直接返回reference;否则抛出NullPointerException异常。
我们使用的时候通常使用第二种,有异常信息更加易读!
3.checkState(boolean,Object):
功能描述:检查对象的一些状态,不依赖方法参数。 例如, Iterator可以用来next是否在remove之前被调用。失败时抛出的异常类型:IllegalStateException
/**
* Ensures the truth of an expression involving the state of the calling instance, but not
* involving any parameters to the calling method.
*
* @param expression a boolean expression
* @param errorMessage the exception message to use if the check fails; will be converted to a
* string using {@link String#valueOf(Object)}
* @throws IllegalStateException if {@code expression} is false
* @see Verify#verify Verify.verify()
*/
public static void checkState(boolean expression, @Nullable Object errorMessage) {
if (!expression) {
throw new IllegalStateException(String.valueOf(errorMessage));
}
}
4.checkElementIndex(int index, int size,String desc):
功能描述:检查index是否为在一个长度为size的list, string或array合法的范围。 index的范围区间是[0, size)(包含0不包含size)。无需直接传入list, string或array, 只需传入大小。返回index。 失败时抛出的异常类型:IndexOutOfBoundsException
/**
* Ensures that {@code index} specifies a valid <i>element</i> in an array, list or string of size
* {@code size}. An element index may range from zero, inclusive, to {@code size}, exclusive.
*
* @param index a user-supplied index identifying an element of an array, list or string
* @param size the size of that array, list or string
* @param desc the text to use to describe this index in an error message
* @return the value of {@code index}
* @throws IndexOutOfBoundsException if {@code index} is negative or is not less than {@code size}
* @throws IllegalArgumentException if {@code size} is negative
*/
@CanIgnoreReturnValue
public static int checkElementIndex(int index, int size, @Nullable String desc) {
// Carefully optimized for execution by hotspot (explanatory comment above)
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException(badElementIndex(index, size, desc));
}
return index;
}
5.checkPositionIndex(int index, int size,String desc):
功能描述:检查位置index是否为在一个长度为size的list, string或array合法的范围。 index的范围区间是[0, size)(包含0不包含size)。无需直接传入list, string或array, 只需传入大小。返回index。失败时抛出的异常类型:IndexOutOfBoundsException
/**
* Ensures that {@code index} specifies a valid <i>position</i> in an array, list or string of
* size {@code size}. A position index may range from zero to {@code size}, inclusive.
*
* @param index a user-supplied index identifying a position in an array, list or string
* @param size the size of that array, list or string
* @param desc the text to use to describe this index in an error message
* @return the value of {@code index}
* @throws IndexOutOfBoundsException if {@code index} is negative or is greater than {@code size}
* @throws IllegalArgumentException if {@code size} is negative
*/
@CanIgnoreReturnValue
public static int checkPositionIndex(int index, int size, @Nullable String desc) {
// Carefully optimized for execution by hotspot (explanatory comment above)
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc));
}
return index;
}
6.checkPositionIndexes(int start, int end, int size):
功能描述:检查[start, end)是一个长度为size的list, string或array合法的范围子集。伴随着错误信息。
失败时抛出的异常类型:IndexOutOfBoundsException
/**
* Ensures that {@code start} and {@code end} specify a valid <i>positions</i> in an array, list
* or string of size {@code size}, and are in order. A position index may range from zero to
* {@code size}, inclusive.
*
* @param start a user-supplied index identifying a starting position in an array, list or string
* @param end a user-supplied index identifying a ending position in an array, list or string
* @param size the size of that array, list or string
* @throws IndexOutOfBoundsException if either index is negative or is greater than {@code size},
* or if {@code end} is less than {@code start}
* @throws IllegalArgumentException if {@code size} is negative
*/
public static void checkPositionIndexes(int start, int end, int size) {
// Carefully optimized for execution by hotspot (explanatory comment above)
if (start < 0 || end < start || end > size) {
throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size));
}
}
一个比较实用实例:
public class GuavaTest {
@Test
public void Preconditions() throws Exception {
getPersonByPrecondition(28, "zhangsan");
try {
getPersonByPrecondition(-28, "zhangsan");
} catch (Exception e) {
System.out.println(e.getMessage());
}
try {
getPersonByPrecondition(28, "");
} catch (Exception e) {
System.out.println(e.getMessage());
}
try {
getPersonByPrecondition(28, null);
} catch (Exception e) {
System.out.println(e.getMessage());
}
List<Integer> intList = new ArrayList<Integer>();
for (int i = 0; i < 10; i++) {
try {
checkState(intList, 9);
intList.add(i);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
try {
checkPositionIndex(intList, 3);
} catch (Exception e) {
System.out.println(e.getMessage());
}
try {
checkPositionIndex(intList, 13);
} catch (Exception e) {
System.out.println(e.getMessage());
}
try {
checkPositionIndexes(intList, 3, 7);
} catch (Exception e) {
System.out.println(e.getMessage());
}
try {
checkPositionIndexes(intList, 3, 17);
} catch (Exception e) {
System.out.println(e.getMessage());
}
try {
checkPositionIndexes(intList, 13, 17);
} catch (Exception e) {
System.out.println(e.getMessage());
}
try {
checkElementIndex(intList, 6);
} catch (Exception e) {
System.out.println(e.getMessage());
}
try {
checkElementIndex(intList, 16);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
private static void getPersonByPrecondition(int age, String neme) throws Exception {
Preconditions.checkNotNull(neme, "neme为null");
Preconditions.checkArgument(neme.length() > 0, "neme为\'\'");
Preconditions.checkArgument(age > 0, "age 必须大于0");
System.out.println("a person age:" + age + ",neme:" + neme);
}
private static void checkState(List<Integer> intList, int index) throws Exception {
//表达式为true不抛异常
Preconditions.checkState(intList.size() < index, " intList size 不能大于" + index);
}
private static void checkPositionIndex(List<Integer> intList, int index) throws Exception {
Preconditions.checkPositionIndex(index, intList.size(), "index " + index + " 不在 list中, List size为:" + intList.size());
}
private static void checkPositionIndexes(List<Integer> intList, int start, int end) throws Exception {
Preconditions.checkPositionIndexes(start, end, intList.size());
}
private static void checkElementIndex(List<Integer> intList, int index) throws Exception {
Preconditions.checkElementIndex(index, intList.size(), "index 为 " + index + " 不在 list中, List size为: " + intList.size());
}
}
输出结果:
a person age:28,neme:zhangsan
age 必须大于0
neme为''
neme为null
intList size 不能大于9
index 13 不在 list中, List size为:9 (13) must not be greater than size (9)
end index (17) must not be greater than size (9)
start index (13) must not be greater than size (9)
index 为 16 不在 list中, List size为: 9 (16) must be less than size (9)
Guava的preconditions有这样几个优点:
在静态导入后, 方法很明确无歧义, checkNotNull可以清楚地告诉你它是干什么的, 它会抛出怎样的异常.
checkNotNull在验证通过后直接返回, 可以这样方便地写代码: this.field = checkNotNull(field).
简单而又强大的可变参数'printf'风格的自定义错误信息.