ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
#### [使用Guava前置条件](https://lingcoder.gitee.io/onjava8/#/book/16-Validating-Your-Code?id=%e4%bd%bf%e7%94%a8guava%e5%89%8d%e7%bd%ae%e6%9d%a1%e4%bb%b6) 在非严格的 DbC 中,前置条件是 DbC 中你不想删除的那一部分,因为它可以检查方法参数的有效性。那是你没有办法控制的事情,所以你需要对其检查。因为 Java 在默认情况下禁用断言,所以通常最好使用另外一个始终验证方法参数的库。 谷歌的 Guava 库包含了一组很好的前置条件测试,这些测试不仅易于使用,而且命名也足够好。在这里你可以看到它们的简单用法。库的设计人员建议静态导入前置条件: ~~~ // validating/GuavaPreconditions.java // Demonstrating Guava Preconditions import java.util.function.*; import static com.google.common.base.Preconditions.*; public class GuavaPreconditions { static void test(Consumer<String> c, String s) { try { System.out.println(s); c.accept(s); System.out.println("Success"); } catch(Exception e) { String type = e.getClass().getSimpleName(); String msg = e.getMessage(); System.out.println(type + (msg == null ? "" : ": " + msg)); } } public static void main(String[] args) { test(s -> s = checkNotNull(s), "X"); test(s -> s = checkNotNull(s), null); test(s -> s = checkNotNull(s, "s was null"), null); test(s -> s = checkNotNull( s, "s was null, %s %s", "arg2", "arg3"), null); test(s -> checkArgument(s == "Fozzie"), "Fozzie"); test(s -> checkArgument(s == "Fozzie"), "X"); test(s -> checkArgument(s == "Fozzie"), null); test(s -> checkArgument( s == "Fozzie", "Bear Left!"), null); test(s -> checkArgument( s == "Fozzie", "Bear Left! %s Right!", "Frog"), null); test(s -> checkState(s.length() > 6), "Mortimer"); test(s -> checkState(s.length() > 6), "Mort"); test(s -> checkState(s.length() > 6), null); test(s -> checkElementIndex(6, s.length()), "Robert"); test(s -> checkElementIndex(6, s.length()), "Bob"); test(s -> checkElementIndex(6, s.length()), null); test(s -> checkPositionIndex(6, s.length()), "Robert"); test(s -> checkPositionIndex(6, s.length()), "Bob"); test(s -> checkPositionIndex(6, s.length()), null); test(s -> checkPositionIndexes( 0, 6, s.length()), "Hieronymus"); test(s -> checkPositionIndexes( 0, 10, s.length()), "Hieronymus"); test(s -> checkPositionIndexes( 0, 11, s.length()), "Hieronymus"); test(s -> checkPositionIndexes( -1, 6, s.length()), "Hieronymus"); test(s -> checkPositionIndexes( 7, 6, s.length()), "Hieronymus"); test(s -> checkPositionIndexes( 0, 6, s.length()), null); } } /* Output: X Success null NullPointerException null NullPointerException: s was null null NullPointerException: s was null, arg2 arg3 Fozzie Success X IllegalArgumentException null IllegalArgumentException null IllegalArgumentException: Bear Left! null IllegalArgumentException: Bear Left! Frog Right! Mortimer Success Mort IllegalStateException null NullPointerException Robert IndexOutOfBoundsException: index (6) must be less than size (6) Bob IndexOutOfBoundsException: index (6) must be less than size (3) null NullPointerException Robert Success Bob IndexOutOfBoundsException: index (6) must not be greater than size (3) null NullPointerException Hieronymus Success Hieronymus Success Hieronymus IndexOutOfBoundsException: end index (11) must not be greater than size (10) Hieronymus IndexOutOfBoundsException: start index (-1) must not be negative Hieronymus IndexOutOfBoundsException: end index (6) must not be less than start index (7) null NullPointerException */ ~~~ 虽然 Guava 的前置条件适用于所有类型,但我这里只演示**字符串(String)**类型。**test()**方法需要一个Consumer,因此我们可以传递一个 lambda 表达式作为第一个参数,传递给 lambda 表达式的字符串作为第二个参数。它显示字符串,以便在查看输出时确定方向,然后将字符串传递给 lambda 表达式。try 块中的第二个**println**() 仅在 lambda 表达式成功时才显示; 否则 catch 块将捕获并显示错误信息。注意**test()**方法消除了多少重复的代码。 每个前置条件都有三种不同的重载形式:一个什么都没有,一个带有简单字符串消息,以及带有一个字符串和替换值。为了提高效率,只允许**%s**(字符串类型)替换标记。在上面的例子中,演示了**checkNotNull()**和**checkArgument()**这两种形式。但是它们对于所有前置条件方法都是相同的。注意**checkNotNull()**的返回参数, 所以你可以在表达式中内联使用它。下面是如何在构造函数中使用它来防止包含**Null**值的对象构造: ~~~ / validating/NonNullConstruction.java import static com.google.common.base.Preconditions.*; public class NonNullConstruction { private Integer n; private String s; NonNullConstruction(Integer n, String s) { this.n = checkNotNull(n); this.s = checkNotNull(s); } public static void main(String[] args) { NonNullConstruction nnc = new NonNullConstruction(3, "Trousers"); } } ~~~ **checkArgument()**接受布尔表达式来对参数进行更具体的测试, 失败时抛出**IllegalArgumentException**,**checkState()**用于测试对象的状态(例如,不变性检查),而不是检查参数,并在失败时抛出**IllegalStateException**。 最后三个方法在失败时抛出**IndexOutOfBoundsException**。**checkElementIndex**() 确保其第一个参数是列表、字符串或数组的有效元素索引,其大小由第二个参数指定。**checkPositionIndex()**确保它的第一个参数在 0 到第二个参数(包括第二个参数)的范围内。**checkPositionIndexes()**检查**\[first\_arg, second\_arg\]**是一个列表的有效子列表,由第三个参数指定大小的字符串或数组。 所有的 Guava 前置条件对于基本类型和对象都有必要的重载。