Java Switch Statement – Java开关语句

最后修改: 2018年 9月 27日

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

1. Overview

1.概述

In this tutorial, we’ll learn what the switch statement is and how to use it.

在本教程中,我们将学习什么是switch语句以及如何使用它。

The switch statement allows us to replace several nested if-else constructs and thus improve the readability of our code.

switch语句允许我们替换几个嵌套的if-else结构,从而提高代码的可读性。

Switch has evolved over time. New supported types have been added, particularly in Java 5 and 7. Also, it continues to evolve — switch expressions will likely be introduced in Java 12.

Switch已经随着时间的推移而不断发展。增加了新的支持类型,特别是在Java 5和7中。而且,它还在继续发展–switch表达式可能会在Java 12中引入。

Below we’ll give some code examples to demonstrate the use of the switch statement, the role of the break statement, the requirements for the switch argument/case values and the comparison of Strings in a switch statement.

下面我们将给出一些代码例子来演示switch语句的使用,break语句的作用,对switch参数/case值的要求以及switch语句中Strings的比较。

Let’s move on to the example.

让我们继续讨论这个例子。

2. Example of Use

2.使用实例

Let’s say we have the following nested if-else statements:

假设我们有以下嵌套 if-else语句。

public String exampleOfIF(String animal) {
    String result;
    if (animal.equals("DOG") || animal.equals("CAT")) {
        result = "domestic animal";
    } else if (animal.equals("TIGER")) {
        result = "wild animal";
    } else {
        result = "unknown animal";
    }
    return result;
}

The above code doesn’t look good and would be hard to maintain and reason about.

上面的代码看起来并不好看,而且会很难维护和推理。

To improve readability, we could make use of a switch statement:

为了提高可读性,我们可以利用一个switch语句。

public String exampleOfSwitch(String animal) {
    String result;
    switch (animal) {
        case "DOG":
            result = "domestic animal"; 
            break;
        case "CAT":
            result = "domestic animal";
            break;
        case "TIGER":
            result = "wild animal";
            break;
        default:
            result = "unknown animal";
            break;
    }
    return result;
}

We compare the switch argument animal with the several case values. If none of the case values is equal to the argument, the block under the default label is executed.

我们将switch参数animal与几个case值进行比较。如果没有一个case值与参数相等,则执行default标签下的块。

Simply put, the break statement is used to exit a switch statement.

简单地说,break语句是用来退出switch语句的。

3. The break Statement

3、break声明

Although most of the switch statements in real life imply that only one of the case blocks should be executed, the break statement is necessary to exit a switch after the block completes.

尽管现实生活中的大多数switch语句都意味着只应执行case块中的一个,但break语句对于在块完成后退出switch是必要的。

If we forget to write a break, the blocks underneath will be executed.

如果我们忘记写一个break,下面的块将被执行。

To demonstrate this, let’s omit the break statements and add the output to the console for each block:

为了证明这一点,让我们省略break语句,将每个块的输出添加到控制台。

public String forgetBreakInSwitch(String animal) {
    switch (animal) {
    case "DOG":
        System.out.println("domestic animal");
    default:
        System.out.println("unknown animal");
    }
}

Let’s execute this code forgetBreakInSwitch(“DOG”) and check the output to prove that all the blocks get executed:

让我们执行这段代码forgetBreakInSwitch(“DOG”),并检查输出以证明所有块都被执行。

domestic animal
unknown animal

So, we should be careful and add break statements at the end of each block unless there is a need to pass through to the code under the next label.

所以,我们应该小心谨慎,在每个块的末尾添加break语句,除非有必要传递到下一个标签下的代码。

The only block where a break is not necessary is the last one, but adding a break to the last block makes the code less error-prone.

唯一不需要break的区块是最后一个,但在最后一个区块中加入break会使代码不容易出错。

We can also take advantage of this behavior to omit break when we want the same code executed for several case statements.

我们还可以利用这一行为,在我们希望几个case语句执行相同的代码时,omit break

Let’s rewrite the example in the previous section by grouping together the first two cases:

让我们把前一节中的例子改写一下,把前两种情况放在一起。

public String exampleOfSwitch(String animal) {
    String result;
    switch (animal) {
        case "DOG":
        case "CAT":
            result = "domestic animal";
            break;
        case "TIGER":
            result = "wild animal";
            break;
        default:
            result = "unknown animal";
            break;
    }
    return result;
}

4. switch Argument and case Values

4.switch论据和case

Now let’s discuss the allowed types of switch argument and case values, the requirements for them and how the switch statement works with Strings.

现在让我们讨论一下switch参数和case值的允许类型,对它们的要求以及switch语句如何与字符串一起工作。

4.1. Data Types

4.1.数据类型

We can’t compare all the types of objects and primitives in the switch statement. A switch works only with four primitives and their wrappers as well as with the enum type and the String class:

我们不能在switch语句中比较所有类型的对象和基元。一个switch只对四个基元和它们的包装器以及enum类型String起作用。

  • byte and Byte
  • short and Short
  • int and Integer
  • char and Character
  • enum
  • String

String type is available in the switch statement starting with Java 7.

从Java 7开始,String 类型在switch语句中可用。

enum type was introduced in Java 5 and has been available in the switch statement since then.

enum类型是在Java 5中引入的,从那时起就可以在switch语句中使用。

Wrapper classes have also been available since Java 5.

从Java 5开始,封装类也已经可以使用了。

Of course, switch argument and case values should be of the same type.

当然,switch参数和case值应该是同一类型。

4.2. No null Values

4.2.没有null

We can’t pass the null value as an argument to a switch statement.

我们不能将null值作为参数传递给switch语句。

If we do, the program will throw NullPointerException, using our first switch example:

如果我们这样做,程序将抛出NullPointerException,使用我们的第一个switch例子。

@Test(expected=NullPointerException.class)
public void whenSwitchAgumentIsNull_thenNullPointerException() {
    String animal = null;
    Assert.assertEquals("domestic animal", s.exampleOfSwitch(animal));
}

Of course, we can’t also pass null as a value to the case label of a switch statement. If we do, the code will not compile.

当然,我们也不能把null作为一个值传递给switch语句的case标签。如果我们这样做,代码将无法编译。

4.3. Case Values as Compile-Time Constants

4.3.Case值作为编译时常量

If we try to replace the DOG  case value with the variable dog, the code won’t compile until we mark the dog variable as final:

如果我们试图用变量dog替换DOG的情况下的值,代码将不会被编译,直到我们将dog变量标记为final

final String dog="DOG";
String cat="CAT";

switch (animal) {
case dog: //compiles
    result = "domestic animal";
case cat: //does not compile
    result = "feline"
}

4.4. String Comparison

4.4.字符串比较

If a switch statement used the equality operator to compare strings, we couldn’t compare a String argument created with the new operator to a String case value correctly.

如果一个switch语句使用平等运算符来比较字符串,我们就不能将String参数new运算符创建的String案例值正确比较。

Luckily, the switch operator uses the equals() method under the hood.

幸运的是,switch操作符在引擎盖下使用equals()方法。

Let’s demonstrate this:

让我们来证明这一点。

@Test
public void whenCompareStrings_thenByEqual() {
    String animal = new String("DOG");
    assertEquals("domestic animal", s.exampleOfSwitch(animal));
}

5. switch Expressions

5.切换表达式

JDK 13 is now available and brings an improved version of a new feature first introduced in JDK 12: the switch expression.

JDK 13现已推出,并带来了JDK 12中首次推出的新功能的改进版本:switch表达式。

In order to enable it, we need to pass –enable-preview to the compiler.

为了启用它,我们需要向编译器传递-enable-preview

5.1. The New switch Expression

5.1.新的switch表达式

Let’s see what the new switch expression looks like when switching over months:

让我们看看新的switch表达方式在切换月份时是什么样子。

var result = switch(month) {
    case JANUARY, JUNE, JULY -> 3;
    case FEBRUARY, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> 1;
    case MARCH, MAY, APRIL, AUGUST -> 2;
    default -> 0; 
};

Sending in a value such as Month.JUNE would set result to 3.

发送一个诸如Month.June的值,将把result设为3

Notice that the new syntax uses the -> operator instead of the colon we’re used to with switch statements. Also, there’s no break keyword: The switch expression doesn’t fall through cases.

请注意,新的语法使用了->操作符,而不是我们习惯的switch语句中的冒号。另外,没有break关键字:switch表达式不会穿过cases。

Another addition is the fact that we can now have comma-delimited criteria.

另一个补充是,我们现在可以有以逗号分隔的标准。

5.2. The yield Keyword

5.2.产量关键词

Going a bit further, there’s a possibility to obtain fine-grain control over what’s happening on the right side of the expression by using code blocks.

再进一步说,有可能通过使用代码块来获得对表达式右侧发生的事情的细粒度控制。

In such a case, we need to use the keyword yield:

在这种情况下,我们需要使用关键字yield

var result = switch (month) {
    case JANUARY, JUNE, JULY -> 3;
    case FEBRUARY, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> 1;
    case MARCH, MAY, APRIL, AUGUST -> {
        int monthLength = month.toString().length();
        yield monthLength * 4;
    }
    default -> 0;
};

While our example is a bit arbitrary, the point is that we’ve got access to more of the Java language here.

虽然我们的例子有点随意,但重点是我们在这里可以接触到更多的Java语言。

5.3. Returning Inside switch Expressions

5.3.在switch表达式内部返回

As a consequence of the distinction between switch statements and switch expressions, it is possible to return from inside a switch statement, but we’re not allowed to do so from within a switch expression.

由于switch语句和switch表达式之间的区别,switch语句中可以return,但我们不允许从switch表达式中这样做。

The following example is perfectly valid and will compile:

下面的例子是完全有效的,并且会被编译。

switch (month) {
    case JANUARY, JUNE, JULY -> { return 3; }
    default -> { return 0; }
}

However, the following code will not compile, as we are trying to return outside of an enclosing switch expression:

然而,下面的代码将不会被编译,因为我们试图在一个封闭的开关表达式之外返回

var result = switch (month) {
    case JANUARY, JUNE, JULY -> { return 3; }
    default -> { return 0; }
};

5.4. Exhaustiveness

5.4.穷尽性

When using switch statements, it doesn’t really matter if all cases are covered.

当使用switch语句时,是否涵盖所有情况其实并不重要。

The following code, for example, is perfectly valid and will compile:

例如,下面的代码是完全有效的,可以编译。

switch (month) { 
    case JANUARY, JUNE, JULY -> 3; 
    case FEBRUARY, SEPTEMBER -> 1;
}

For switch expressions though, the compiler insists that all possible cases are covered.

但对于switch表达式,编译器坚持认为所有可能的情况都被涵盖了。

The following code snippet would not compile, as there’s no default case and not all possible cases are covered:

下面的代码片断不会被编译,因为没有默认情况,也没有涵盖所有可能的情况。

var result = switch (month) {
    case JANUARY, JUNE, JULY -> 3;
    case FEBRUARY, SEPTEMBER -> 1;
}

The switch expression, however, will be valid when all possible cases are covered:

然而,switch表达式在涵盖所有可能的情况时将是有效的。

var result = switch (month) {
    case JANUARY, JUNE, JULY -> 3;
    case FEBRUARY, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER -> 1;
    case MARCH, MAY, APRIL, AUGUST -> 2;
}

Please note that the above code snippet does not have a default case. As long as all cases are covered, the switch expression will be valid.

请注意,上面的代码片断没有default情况。只要涵盖所有情况,switch表达式就会有效。

6. Conclusion

6.结论

In this article, we discussed the subtleties of using the switch statement in Java. We can decide whether to use switch based on readability and the type of the compared values.

在这篇文章中,我们讨论了在Java中使用switch语句的微妙之处。我们可以根据可读性和比较值的类型来决定是否使用switch

The switch statement is a good candidate for cases when we have a limited number of options in a predefined set (e.g., days of the week).

当我们在一个预定义的集合中拥有有限数量的选项时(如一周中的几天),switch语句是一个很好的候选方案。

Otherwise, we’d have to modify the code each time a new value is added or removed, which may not be feasible. For these cases, we should consider other approaches such as polymorphism or other design patterns such as Command.

否则,每次添加或删除新的值时,我们都必须修改代码,这可能是不可行的。对于这些情况,我们应该考虑其他方法,如polymorphism或其他设计模式,如Command

As always, the complete JDK 8 code and JDK 13 code is available on GitHub.

一如既往,完整的JDK 8代码JDK 13代码可以在GitHub上找到。