Guava 基本库

Guava 项目包含了谷歌在基于 Java 的项目中依赖的几个 Google 核心库:集合,缓存,原语支持,并发库,常见注释,字符串处理,I/O 等。
  • 基本工具类:让使用 Java 语言更令人愉悦
    • 使用和避免 null:null 有语言歧义, 会产生令人费解的错误, 反正他总是让人不爽。很多 Guava 的工具类在遇到 null 时会直接拒绝或出错,而不是默默地接受他们。
    • 前提条件:更容易的对你的方法进行前提条件的测试。
    • 常见的对象方法: 简化了 Object 常用方法的实现, 如 hashCode() 和 toString()。
    • 排序: Guava 强大的 "fluent Comparator"比较器, 提供多关键字排序。
    • Throwable 类: 简化了异常检查和错误传播。
  • 集合类:集合类库是 Guava 对 JDK 集合类的扩展, 这是 Guava 项目最完善和为人所知的部分。
    • Immutable collections(不变的集合): 防御性编程, 不可修改的集合,并且提高了效率。
    • New collection types(新集合类型):JDK collections 没有的一些集合类型,主要有:multisets,multimaps,tables, bidirectional maps 等等
    • Powerful collection utilities(强大的集合工具类): java.util.Collections 中未包含的常用操作工具类
    • Extension utilities(扩展工具类):给 Collection 对象添加一个装饰器还是实现迭代器? 我们可以更容易的实现这些。
  • 缓存:本地缓存,可以很方便的操作缓存对象,并且支持各种缓存失效行为模式。
  • Functional idioms(函数式):简洁, Guava 实现了 Java 的函数式编程,可以显著简化代码。
  • Concurrency(并发):强大,简单的抽象,让我们更容易实现简单正确的并发性代码
    • ListenableFuture(可监听的 Future): Futures,用于异步完成的回调。
    • Service: 控制事件的启动和关闭,为你管理复杂的状态逻辑。
  • Strings:一个非常非常有用的字符串工具类: 提供 splitting,joining, padding 等操作。
  • Primitives:扩展 JDK 中未提供的对原生类型(如 int、char 等)的操作, 包括某些类型的无符号的变量。
  • Ranges:Guava 一个强大的 API,提供 Comparable 类型的范围处理, 包括连续和离散的情况。
  • I/O:简化 I/O 操作, 特别是对 I/O 流和文件的操作, for Java 5 and 6。
  • Hashing:提供比 Object.hashCode() 更复杂的 hash 方法, 提供 Bloom filters。
  • EventBus:基于发布-订阅模式的组件通信,但是不需要明确地注册在委托对象中。
  • Math:优化的 math 工具类,经过完整测试。
  • Reflection:Guava 的 Java 反射机制工具类。

Preconditions

Preconditions 类提供了一个静态方法列表,用于检查方法或构造函数是否以有效的参数值被调用。如果一个先决条件失败,就会抛出一个定制的异常。Preconditions 类中的每个静态方法都有三个变体:

  • 没有参数,抛出的异常没有错误信息。
  • 一个额外的 Object 参数作为错误信息。异常会被抛出一个错误信息。
  • 一个额外的字符串参数,以及任意数量的额外对象参数作为一个占位符的错误信息。它的行为有点像 printf,但为了 GWT 的兼容性和效率,它只允许使用 %s 指示器

我们来看看如何使用 Preconditions 类。

checkArgument

Preconditions 类的方法 checkArgument 确保传递给调用方法的参数的真实性。该方法接受一个布尔条件,并在条件为假时抛出一个 IllegalArgumentException。我们可以在不向 checkArgument 方法传递任何额外参数的情况下使用 checkArgument:

@Test
public void whenCheckArgumentEvaluatesFalse_throwsException() {
    int age = -18;
    assertThatThrownBy(() -> Preconditions.checkArgument(age > 0))
      .isInstanceOf(IllegalArgumentException.class)
      .hasMessage(null).hasNoCause();
}Copy to clipboardErrorCopied

我们可以通过传递错误信息从 checkArgument 方法中得到一个有意义的错误信息:

@Test
public void givenErrorMsg_whenCheckArgEvalsFalse_throwsException() {
    int age = -18;
    String message = "Age can't be zero or less than zero.";
    assertThatThrownBy(() -> Preconditions.checkArgument(age > 0, message))
      .isInstanceOf(IllegalArgumentException.class)
      .hasMessage(message).hasNoCause();
}Copy to clipboardErrorCopied

我们可以通过传递一个错误信息,从 checkArgument 方法中得到一个有意义的错误信息以及动态数据:

@Test
public void givenTemplateMsg_whenCheckArgEvalsFalse_throwsException() {
    int age = -18;
    String message = "Age should be positive number, you supplied %s.";
    assertThatThrownBy(
      () -> Preconditions.checkArgument(age > 0, message, age))
      .isInstanceOf(IllegalArgumentException.class)
      .hasMessage(message, age).hasNoCause();
}Copy to clipboardErrorCopied

checkElementIndex

方法 checkElementIndex 检查一个索引是否是列表、字符串或指定大小的数组中的有效索引。元素索引的范围可以从 0 到大小不等。你不需要直接传递一个 list、字符串或数组,你只需要传递它的大小。如果索引不是有效的元素索引,这个方法就会抛出 IndexOutOfBoundsException,否则就会返回一个正在传递给方法的索引。

让我们看看如何使用这个方法,通过在检查 ElementIndex 方法抛出异常时传递一个错误信息来显示它的有意义的错误信息:

@Test
public void givenArrayAndMsg_whenCheckElementEvalsFalse_throwsException() {
    int[] numbers = { 1, 2, 3, 4, 5 };
    String message = "Please check the bound of an array and retry";
    assertThatThrownBy(() ->
      Preconditions.checkElementIndex(6, numbers.length - 1, message))
      .isInstanceOf(IndexOutOfBoundsException.class)
      .hasMessageStartingWith(message).hasNoCause();
}Copy to clipboardErrorCopied

checkNotNull

方法 checkNotNull 检查作为参数提供的值是否为空。它返回被检查的值。如果传递给这个方法的值是空的,那么就会抛出一个 NullPointerException。接下来,我们将展示如何使用这个方法,通过传递错误信息,从 checkNotNull 方法中获取有意义的错误信息:

@Test
public void givenNullString_whenCheckNotNullWithMessage_throwsException () {
    String nullObject = null;
    String message = "Please check the Object supplied, its null!";
    assertThatThrownBy(() -> Preconditions.checkNotNull(nullObject, message))
      .isInstanceOf(NullPointerException.class)
      .hasMessage(message).hasNoCause();
}Copy to clipboardErrorCopied

我们还可以通过向错误信息传递一个参数,从 checkNotNull 方法中得到一个基于动态数据的有意义的错误信息:

@Test
public void whenCheckNotNullWithTemplateMessage_throwsException() {
    String nullObject = null;
    String message = "Please check the Object supplied, its %s!";
    assertThatThrownBy(
      () -> Preconditions.checkNotNull(nullObject, message,
        new Object[] { null }))
      .isInstanceOf(NullPointerException.class)
      .hasMessage(message, nullObject).hasNoCause();
}Copy to clipboardErrorCopied

checkPositionIndex

方法 checkPositionIndex 检查作为参数传递给本方法的索引是否是指定大小的列表、字符串或数组中的有效索引。一个位置索引的范围可以从 0(含)到大小(含)。你不需要直接传递列表、字符串或数组,你只需要传递它的大小。

如果传递的索引不在 0 和给定的大小之间,这个方法就会抛出一个 IndexOutOfBoundsException,否则就会返回索引值。让我们看看如何从 checkPositionIndex 方法中得到有意义的错误信息。

@Test
public void givenArrayAndMsg_whenCheckPositionEvalsFalse_throwsException() {
    int[] numbers = { 1, 2, 3, 4, 5 };
    String message = "Please check the bound of an array and retry";
    assertThatThrownBy(
      () -> Preconditions.checkPositionIndex(6, numbers.length - 1, message))
      .isInstanceOf(IndexOutOfBoundsException.class)
      .hasMessageStartingWith(message).hasNoCause();
}Copy to clipboardErrorCopied

checkState

方法 checkState 检查对象状态的有效性,并且不依赖于方法的参数。例如,一个 Iterator 可能会使用这个方法来检查在调用 remove 之前是否已经调用了 next。如果对象的状态(作为方法参数传递的布尔值)处于无效状态,该方法会抛出一个 IllegalStateException。

让我们看看如何使用这个方法,通过在 checkState 方法抛出异常时传递一个错误信息来显示它的有意义的错误信息。

@Test
public void givenStatesAndMsg_whenCheckStateEvalsFalse_throwsException() {
    int[] validStates = { -1, 0, 1 };
    int givenState = 10;
    String message = "You have entered an invalid state";
    assertThatThrownBy(
      () -> Preconditions.checkState(
        Arrays.binarySearch(validStates, givenState) > 0, message))
      .isInstanceOf(IllegalStateException.class)
      .hasMessageStartingWith(message).hasNoCause();
}