概述

在这个教程中,我们将展示如何在Google Guava库中使用Preconditions类。

Preconditions类提供了一系列静态方法,用于检查方法或构造函数是否被调用时传入了有效的参数值。如果预条件失败,将抛出一个定制的异常。


2. Google Guava的Preconditions

Preconditions类中的每个静态方法都有三种变体:

  • 无参数:异常在没有错误消息的情况下被抛出
  • 多了一个Object参数作为错误消息。异常带有错误消息被抛出
  • 多了一个字符串参数,后面跟着任意数量的Object参数,它们作为占位符的错误消息。这有点像printf,但由于GWT兼容性和效率考虑,它只允许使用%s指示符

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

2.1. Maven依赖

首先,在pom.xml中添加Google Guava库的依赖:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.3-jre</version>
</dependency>

你可以在这里查看最新的依赖版本:此处


3. checkArgument

Preconditions类的checkArgument方法确保调用方法传递的参数为真。此方法接受一个布尔条件,当条件为假时抛出IllegalArgumentException

3.1. 无错误消息

我们可以在不向checkArgument方法传递额外参数的情况下使用它:

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

3.2. 带有错误消息

通过传递错误消息,我们可以从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();
}

3.3. 带有模板错误消息

我们可以通过传递错误消息和动态数据从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();
}

4. checkElementIndex

checkElementIndex方法检查给定的索引是否是列表、字符串或指定大小数组的有效索引。元素索引范围从0(包括)到大小(不包括)。你不需要直接传递列表、字符串或数组,只需传递其大小。如果索引不是有效元素索引,此方法将抛出IndexOutOfBoundsException,否则返回传递给方法的索引值。

让我们通过显示当它抛出异常时的有意义错误消息来演示如何使用此方法:

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

5. checkNotNull

checkNotNull方法检查作为参数传递的值是否为null。它会返回已检查的值。如果传递给此方法的值为null,则抛出NullPointerException

接下来,我们将展示如何使用此方法,通过传递错误消息来获取有意义的错误消息:

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

我们还可以根据动态数据从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();
}

6. 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();
}

7. checkState

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

让我们通过在抛出异常时显示有意义的错误消息来展示如何使用此方法:

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

8. 总结

在这篇教程中,我们详细介绍了Guava库中PreConditions类的方法。Preconditions类提供了一组静态方法,用于验证方法或构造函数是否以有效的参数值被调用。

上述示例的代码可以在GitHub项目中找到——这是一个基于Maven的项目,可以直接导入并运行。