Check if an Enum Value Exists in Java – 在Java中检查一个枚举值是否存在

最后修改: 2021年 12月 15日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

We see enumerations in almost every application. These include order status codes, like DRAFT and PROCESSING, and web error codes, like 400, 404, 500, 501, etc. Whenever we see enumerated data in the domain, we’ll see Enum for it in our application. We can use the data in an incoming request and find that enum. For example, we can map web error 400 to BAD_REQUEST.

我们几乎在每个应用中都能看到枚举。这些包括订单状态代码,如DRAFTPROCESSING,以及网络错误代码,如400、404、500、501等。只要我们在域中看到枚举数据,我们就会在我们的应用程序中看到它的Enum。我们可以使用传入请求中的数据,并找到该枚举。例如,我们可以将网络错误400映射到BAD_REQUEST

As such, we need logic to search the enum by criteria. This could be its name or its value. Alternatively, it could even be an arbitrary integer code.

因此,我们需要通过标准来搜索枚举的逻辑。这可以是它的名字或它的值。或者,它甚至可以是一个任意的整数代码。

In this tutorial, we’ll learn how to search an enum by criteria. In addition, we’ll also explore different ways to return the enum found.

在本教程中,我们将学习如何按标准搜索一个枚举。此外,我们还将探索不同的方式来返回找到的枚举。

2. Searching an Enum by Name

2.按名称搜索枚举

To begin with, we know that an enum type is a special data type. It enables a variable to be a set of predefined constants. Let’s define an enum for direction:

首先,我们知道枚举类型是一种特殊的数据类型。它可以使一个变量成为一组预定义的常量。让我们为方向定义一个枚举。

public enum Direction {
    EAST, WEST, SOUTH, NORTH;
}

The name of the enum value is constant. So for instance, the name of Direction.EAST is EAST. Now we can search the direction by its name. It’s a good idea to implement a case-insensitive search. As a result, East, east, and EAST would all map to Direction.EAST. Let’s add the following method to Direction enum:

枚举值的名称是常数。因此,例如,Direction.EAST的名称是EAST。现在我们可以通过它的名字来搜索方向。实现不区分大小写的搜索是一个好主意。因此,EasteastEAST都会映射到Direction.EAST。让我们在Direction枚举中添加以下方法。

public static Direction findByName(String name) {
    Direction result = null;
    for (Direction direction : values()) {
        if (direction.name().equalsIgnoreCase(name)) {
            result = direction;
            break;
        }
    }
    return result;
}

In this implementation, we’re returning null if we don’t find the enum for the given name. It’s up to us how we treat the not-found scenario. One option is that we can return a default enum value. Conversely, we can throw an exception. We’ll see more examples of searching the enum shortly. Now let’s test our search logic. First, the positive scenario:

在这个实现中,如果我们没有找到指定名称的枚举,我们将返回null。这取决于我们如何处理未找到的情况。一种选择是,我们可以返回一个默认的枚举值。反之,我们可以抛出一个异常。我们很快会看到更多搜索枚举的例子。现在让我们测试一下我们的搜索逻辑。首先,正面的情况。

@Test
public void givenWeekdays_whenValidDirectionNameProvided_directionIsFound() {
    Direction result = Direction.findByName("EAST");
    assertThat(result).isEqualTo(Direction.EAST);
}

At the end of this article, we’ll provide the link to the complete code implementation, but right now, we’re going to focus on the code snippets. Here we searched the direction for the name “EAST” and we expect to get Direction.EAST. As mentioned earlier, we know that the search is not case sensitive, so we should get the same result for the name “east” or “East”. Let’s validate our expectations:

在本文的最后,我们将提供完整代码实现的链接,但现在,我们要关注的是代码片段。在这里,我们搜索了名称为 “EAST “的方向,我们期望得到Direction.EAST。如前所述,我们知道搜索是不分大小写的,所以对于 “东 “或 “东 “这个名字我们应该得到同样的结果。让我们来验证一下我们的期望。

@Test
public void givenWeekdays_whenValidDirectionNameLowerCaseProvided_directionIsFound() {
    Direction result = Direction.findByName("east");
    assertThat(result).isEqualTo(Direction.EAST);
}

We could also add one more test to validate if the search method returns the same result for the name “East”. The following test would illustrate that we get the same result for the name “East”.

我们还可以增加一个测试来验证搜索方法是否对 “East “这个名字返回相同的结果。下面的测试将说明我们对 “东 “这个名字得到了相同的结果。

@Test public void givenWeekdays_whenValidDirectionNameLowerCaseProvided_directionIsFound() { 
    Direction result = Direction.findByName("East"); 
    assertThat(result).isEqualTo(Direction.EAST); 
}

3. Searching an Enum by Value

3.通过值搜索枚举

Now let’s define an enum for the days in a week. This time, let’s provide a value along with the name. In fact, we can define any data member(s) inside the enum, and then use it for our application logic. Here’s the code for the Weekday enum:

现在让我们为一周内的日子定义一个枚举。这一次,让我们在提供名称的同时提供一个值。事实上,我们可以在枚举中定义任何数据成员,然后将其用于我们的应用逻辑。下面是Weekday枚举的代码。

public Weekday {
    MONDAY("Monday"),
    TUESDAY("Tuesday"),
    // ...
    SUNDAY("Sunday"),
    ;
    private final String value;

    Weekday(String value) {
        this.value = value;
    }
}

Next, let’s implement the search by the value. So for “Monday” we should get Weekday.MONDAY. Let’s add the following method to the enum:

接下来,让我们实现按值搜索。所以对于 “星期一”,我们应该得到Weekday.MONDAY。让我们在枚举中添加以下方法。

public static Weekday findByValue(String value) {
    Weekday result = null;
    for (Weekday day : values()) {
        if (day.getValue().equalsIgnoreCase(value)) {
            result = day;
            break;
        }
    }
    return result;
}

Here we’re iterating over the constants of the enum and then comparing the value input to the value member of the enum. As mentioned earlier, we’re ignoring the case of the value. Now we can test it:

在这里,我们正在遍历枚举的常量,然后将输入的值与枚举的值成员进行比较。如前所述,我们忽略了值的情况。现在我们可以测试它了。

@Test
public void givenWeekdays_whenValidWeekdayValueProvided_weekdayIsFound() {
    Weekday result = Weekday.findByValue("Monday");
    assertThat(result).isEqualTo(Weekday.MONDAY);
}

If we don’t provide a valid value, we’ll get null in return. Let’s validate this:

如果我们不提供一个有效的值,我们将得到null的回报。让我们来验证一下。

@Test
public void givenWeekdays_whenInvalidWeekdayValueProvided_nullIsReturned() {
    Weekday result = Weekday.findByValue("mon");
    assertThat(result).isNull();
}

The search doesn’t always need to be by string values. That would be quite inconvenient, as we would have to convert the input into a string first and then pass it to the search method. Now let’s see how to search by non-string values, such as an integer value.

搜索并不总是需要通过字符串值来进行。这将是相当不方便的,因为我们必须先把输入值转换成字符串,然后再把它传递给搜索方法。现在让我们来看看如何通过非字符串值进行搜索,比如说整数值。

4. Searching an Enum by Integer Value

4.通过整数值搜索一个枚举

Let’s define a new enum called Month. Here’s the code for the Month enum:

让我们定义一个名为Month的新枚举。下面是Month枚举的代码。

public enum Month {
    JANUARY("January", 1),
    FEBRUARY("February", 2),
    // ...
    DECEMBER("December", 12),
    ;

    private final String value;
    private final int code;

    Month(String value, int code) {
        this.value = value;
        this.code = code;
    }
}

We can see that the month enum has two members, the value and the code, with the code being an integer value. Let’s implement the logic to search the months by their code:

我们可以看到,月份枚举有两个成员,值和代码,其中代码是一个整数值。让我们来实现按代码搜索月份的逻辑。

public static Optional<Month> findByCode(int code) {
    return Arrays.stream(values()).filter(month -> month.getCode() == code).findFirst();
}

This search looks a little different from the previous searches because we’ve used Java 8 features to demonstrate another way to implement the search. Here, instead of returning the enum itself, we’ll return an Optional value of the enum. Similarly, instead of null, we’ll return an empty Optional. So if we search a month for code 1, we should get Month.JANUARY. Let’s validate this with a test:

这个搜索看起来与之前的搜索有些不同,因为我们使用了Java 8的特性来演示另一种实现搜索的方式。在这里,我们不是返回枚举本身,而是返回该枚举的Optional值。同样地,我们不返回null,而是返回一个空的Optional。因此,如果我们搜索代码为1的月份,我们应该得到Month.JanuARY。让我们用一个测试来验证这一点。

@Test
public void givenMonths_whenValidMonthCodeProvided_optionalMonthIsReturned() {
    Optional<Month> result = Month.findByCode(1);
    assertThat(result).isEqualTo(Optional.of(Month.JANUARY));
}

For invalid code values, we should get an empty Optional. Let’s validate this with a test as well:

对于无效的代码值,我们应该得到一个空的Optional。让我们也用一个测试来验证一下。

@Test
public void givenMonths_whenInvalidMonthCodeProvided_optionalEmptyIsReturned() {
    Optional<Month> result = Month.findByCode(0);
    assertThat(result).isEmpty();
}

There may be cases where we want to implement a stricter search. As such, we wouldn’t tolerate invalid inputs and we’d throw exceptions to demonstrate this.

在有些情况下,我们可能想实现更严格的搜索。因此,我们不会容忍无效的输入,我们会抛出异常来证明这一点。

5. Exceptions Thrown from Search Methods

5.从搜索方法中抛出的异常情况

Instead of returning null or empty Optional value, we may want to throw an exception. Which exception to throw totally depends on the needs of the system. We’ll choose to throw an IllegalArgumentException if we don’t find the enum. Here’s the code for the search method:

我们可能想抛出一个异常,而不是返回null或空的Optional值。抛出哪个异常完全取决于系统的需要。如果我们没有找到这个枚举,我们会选择抛出一个IllegalArgumentException。下面是搜索方法的代码。

public static Month findByValue(String value) {
    return Arrays.stream(values()).filter(month -> month.getValue().equalsIgnoreCase(value)).findFirst().orElseThrow(IllegalArgumentException::new);
}

We can see again that we’re using the Java 8 style while throwing the exception. Let’s validate it with a test:

我们可以再次看到,我们在抛出异常时使用了Java 8的风格。让我们用一个测试来验证它。

@Test
public void givenMonths_whenInvalidMonthValueProvided_illegalArgExIsThrown() {
    assertThatIllegalArgumentException().isThrownBy(() -> Month.findByValue("Jan"));
}

The search methods demonstrated in this article aren’t the only way to do it, but they represent the most common options. We can also tweak these implementations to suit our system’s needs.

本文所演示的搜索方法并不是唯一的方法,但它们代表了最常见的选项。我们还可以对这些实现进行调整,以适应我们系统的需要。

6. Conclusion

6.结语

In this article, we learned various ways to search enums. We also discussed the different ways to return the results. Finally, we backed those implementations with solid unit tests.

在这篇文章中,我们学习了搜索枚举的各种方法。我们还讨论了返回结果的不同方式。最后,我们用可靠的单元测试来支持这些实现。

As always, the code relating to this article is available over on GitHub.

一如既往,与本文有关的代码可在GitHub上获得over