[Guava源码日报](4)Preconditions

时间:2022-05-12 20:43:14

Preconditions是guava提供的用于进行代码校验的工具类,其中提供了许多重要的静态校验方法,用来简化我们工作或开发中对代码的校验或预 处理,能够确保代码符合我们的期望,并且能够在不符合校验条件的地方,准确的为我们显示出问题所在,接下来,我们就来学习使用Preconditions 进行代码校验。

我们可以轻松的写出我们自己的先决条件,如下:

public static Object checkNotNull(Object object,String message){
if(object == null){
throw new IllegalArgumentException(message);
}//if
return object;
}

但是使用先决条件(静态导入)来修改上述检查参数是否为null更为简洁:

checkNotNull(object,"not be null");

Guava为我们提供了更好的封装,使用起来更加简洁,出错率更低。

下面我们局一个简单的例子:

package com.qunar.test;import static com.google.common.base.Preconditions.*;/** * Created by xiaosi on 16-3-6. */public class PreconditionExample {    private String str;    private int[] values = new int[5];    private int currentIndex;    public PreconditionExample(String str){        this.str = checkNotNull(str,"str cant not be null");    }    public void updateCurrentIndexValue(int index,int value){        this.currentIndex = checkElementIndex(index,values.length,"Index out of bounds for values");        checkArgument(value <= 100,"value cant not be more than 100");        values[index] = value;    }    public void doOperation(){        checkState(validateObjectState(),"cant not perform operation");    }    private boolean validateObjectState(){        return this.str.equalsIgnoreCase("open") && values[this.currentIndex] == 10;    }}

Guava进行了大量方法的重载,组成了Preconditions工具类,下面我们先简单的了解一下静态方法。

(1)用来校验表达式是否为真,一般用作方法中校验参数

public static void checkArgument(boolean expression) {    if (!expression) {      throw new IllegalArgumentException();    }  }

例如上面例子中,检验参数是否小于等于100:

public void updateCurrentIndexValue(int index,int value){        this.currentIndex = checkElementIndex(index,values.length,"Index out of bounds for values");        checkArgument(value <= 100,"value cant not be more than 100");        values[index] = value;    }

(2)校验表达式是否为真,不为真时显示指定的错误信息。

public static void checkArgument(boolean expression, @Nullable Object errorMessage) {    if (!expression) {      throw new IllegalArgumentException(String.valueOf(errorMessage));    }  }

(3)校验表达式是否为真,不为真时为你指定的错误信息模板,并且可以使用可变长参数。

public static void checkArgument(boolean expression,      @Nullable String errorMessageTemplate,      @Nullable Object... errorMessageArgs) {    if (!expression) {      throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs));    }  }

这个方法调用了format方法,根据异常信息模板生成异常信息。

(4)检查对象的一些状态,不包括方法参数 (不是很理解)

public static void checkState(boolean expression) {    if (!expression) {      throw new IllegalStateException();    }  }

例如上面例子:

public void doOperation(){        checkState(validateObjectState(),"cant not perform operation");    }    private boolean validateObjectState(){        return this.str.equalsIgnoreCase("open") && values[this.currentIndex] == 10;    }

(5)校验对象是否为空

public static <T> T checkNotNull(T reference) {    if (reference == null) {      throw new NullPointerException();    }    return reference;  }

(6)checkElementIndex( int index, int size, @Nullable String desc)

校验元素的索引值是否有效,index大于等于0小于size,在无效时显示给定的错误描述信息。

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;  }

具体异常信息生成函数:

private static String badElementIndex(int index, int size, String desc) {    if (index < 0) {      return format("%s (%s) must not be negative", desc, index);    } else if (size < 0) {      throw new IllegalArgumentException("negative size: " + size);    } else { // index >= size      return format("%s (%s) must be less than size (%s)", desc, index, size);    }  }

(7)checkPositionIndex

检验index作为位置值对某个列表、字符串或数组是否有效。index>=0 && index<=size

public static int checkPositionIndex(int index, int size) {    return checkPositionIndex(index, size, "index");  }

重载函数 提供异常描述信息

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;  }

重载函数 检验[start, end]表示的位置范围对某个列表、字符串或数组是否有效

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));    }  }

检验函数 检验index下标是否有效

/** * 下标异常信息 * @param index 当前下标 * @param size 下标长度 * @param desc 描述 * @return 合成下标异常信息 */    private static String badPositionIndex(int index, int size, String desc) {        if (index < 0) {            return format("%s (%s) must not be negative", desc, index);        }//if        else if (size < 0) {            throw new IllegalArgumentException("negative size: " + size);        }//else        else { // index > size            return format("%s (%s) must not be greater than size (%s)", desc, index, size);        }//else    }

重载函数 检验[start, end]表示的位置范围是否有效

private static String badPositionIndexes(int start, int end, int size) {    if (start < 0 || start > size) {      return badPositionIndex(start, size, "start index");    }    if (end < 0 || end > size) {      return badPositionIndex(end, size, "end index");    }    // end < start    return format("end index (%s) must not be less than start index (%s)", end, start);  }

(8)format

格式化字符串,将template中的每一个"%s"占位符用args参数替换。第一个%s使用args[0]替换,以此类推。

举例:

template:%s (%s) must not be greater than size (%s)args:"array index"85resultarray index (8) must not be greater than size (5)

源码:

public static String format(String template, Object... args) {        template = String.valueOf(template); // null -> "null"        // StringBuilder builder = new StringBuilder() ?        StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);        int templateStart = 0;        int i = 0;        // 用参数替换"%s"占位符        while (i < args.length) {            // 寻找"%s"下标位置            int placeholderStart = template.indexOf("%s", templateStart);            // 未找到            if (placeholderStart == -1) {                break;            }//if            builder.append(template.substring(templateStart, placeholderStart));            builder.append(args[i++]);            // 跳过"%s"占位符            templateStart = placeholderStart + 2;        }//while        builder.append(template.substring(templateStart));        // 多余参数 添加在方括号内 [args1,args2,...]        if (i < args.length) {            builder.append(" [");            builder.append(args[i++]);            while (i < args.length) {                builder.append(", ");                builder.append(args[i++]);            }//while            builder.append(']');        }//if        return builder.toString();    }

我们之所以选择Guava的Preconditions作为首选:

在静态导入后,Guava方法非常清楚明了。checkNotNull清楚地描述做了什么,会抛出什么异常;

checkNotNull直接返回检查的参数,让你可以在构造函数中保持字段的单行赋值风格:this.field = checkNotNull(field)

简单的、参数可变的printf风格异常信息。鉴于这个优点,在JDK7已经引入Objects.requireNonNull的情况下,我们仍然建议你使用checkNotNull。