为什么我们需要突破案例陈述?

问题描述 投票:88回答:17

为什么编译器不会在交换机中的每个代码块之后自动放置break语句?这是出于历史原因吗?您何时需要执行多个代码块?

java switch-statement case language-design break
17个回答
90
投票

有时,将多个案例与相同的代码块相关联是有帮助的,例如

case 'A':
case 'B':
case 'C':
    doSomething();
    break;

case 'D':
case 'E':
    doSomethingElse();
    break;

等等只是一个例子。

根据我的经验,通常情况下“糟透了”并且在一个案例中执行多个代码块是不好的风格,但在某些情况下可能会有用途。


3
投票

切换breaks之后的case用于避免switch语句中的漏洞。虽然有趣的是,现在可以通过JEP-325实现的新形成的开关标签来实现。

有了这些变化,可以避免每个开关breakcase进一步证明: -

public class SwitchExpressionsNoFallThrough {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int value = scanner.nextInt();
        /*
         * Before JEP-325
         */
        switch (value) {
            case 1:
                System.out.println("one");
            case 2:
                System.out.println("two");
            default:
                System.out.println("many");
        }

        /*
         * After JEP-325
         */
        switch (value) {
            case 1 ->System.out.println("one");
            case 2 ->System.out.println("two");
            default ->System.out.println("many");
        }
    }
}

executing the above code with JDK-12,比较产出可以被视为

//input
1
// output from the implementation before JEP-325
one
two
many
// output from the implementation after JEP-325
one

//input
2
// output from the implementation before JEP-325
two
many
// output from the implementation after JEP-325
two

当然事情没有改变

// input
3
many // default case match
many // branches to 'default' as well

1
投票

正如人们之前所说的那样,它是允许堕落而不是错误,这是一个特征。如果太多break语句惹恼你,你可以通过使用return语句轻松摆脱它们。这实际上是一个很好的做法,因为你的方法应该尽可能小(为了可读性和可维护性),所以一个switch语句已经足够大了一个方法,因此,一个好的方法不应该包含任何其他东西,这个是一个例子:

public class SwitchTester{
    private static final Log log = LogFactory.getLog(SwitchTester.class);
    public static void main(String[] args){
        log.info(monthsOfTheSeason(Season.WINTER));
        log.info(monthsOfTheSeason(Season.SPRING));
        log.info(monthsOfTheSeason(Season.SUMMER));
        log.info(monthsOfTheSeason(Season.AUTUMN));
    }

    enum Season{WINTER, SPRING, SUMMER, AUTUMN};

    static String monthsOfTheSeason(Season season){
        switch(season){
            case WINTER:
                return "Dec, Jan, Feb";
            case SPRING:
                return "Mar, Apr, May";
            case SUMMER:
                return "Jun, Jul, Aug";
            case AUTUMN:
                return "Sep, Oct, Nov";
            default:
                //actually a NullPointerException will be thrown before reaching this
                throw new IllegalArgumentException("Season must not be null");
        }        
    }
}   

执行打印:

12:37:25.760 [main] INFO lang.SwitchTester - Dec, Jan, Feb
12:37:25.762 [main] INFO lang.SwitchTester - Mar, Apr, May
12:37:25.762 [main] INFO lang.SwitchTester - Jun, Jul, Aug
12:37:25.762 [main] INFO lang.SwitchTester - Sep, Oct, Nov

正如所料。


0
投票

编译器没有添加自动中断,可以通过从1和2中删除break语句来使用switch / case来测试1 <= a <= 3等条件。

switch(a) {
  case 1: //I'm between 1 and 3
  case 2: //I'm between 1 and 3
  case 3: //I'm between 1 and 3
          break;
}

0
投票

因为有些情况下你想要流过第一个块,例如为了避免在多个块中写入相同的代码,但仍然可以将它们分开以进行mroe控制。还有很多其他原因。


0
投票

这是一个老问题,但实际上我今天遇到了没有破坏声明的情况。当您需要按顺序组合不同的函数时,不使用break实际上非常有用。

例如使用http响应代码来验证具有时间令牌的用户

服务器响应代码401 - 令牌已过时 - >重新生成令牌并登录用户。 服务器响应代码200 - 令牌正常 - >登录用户。

在案件陈述中:

case 404:
case 500:
        {
            Log.v("Server responses","Unable to respond due to server error");
            break;
        }
        case 401:
        {
             //regenerate token
        }
        case 200:
        {
            // log in user
            break;
        }

使用此方法,您无需为401响应调用登录用户功能,因为当重新生成令牌时,运行时跳转到案例200中。


0
投票

您可以轻松地分离其他类型的数字,月份,计数。 如果在这种情况下,那就更好了;

public static void spanishNumbers(String span){

    span = span.toLowerCase().replace(" ", "");
    switch (span){
     case "1":    
     case "jan":  System.out.println("uno"); break;    
     case "2":      
     case "feb":  System.out.println("dos"); break;    
     case "3":     
     case "mar":  System.out.println("tres"); break;   
     case "4":   
     case "apr":  System.out.println("cuatro"); break;
     case "5":    
     case "may":  System.out.println("cinco"); break;
     case "6":     
     case "jun":  System.out.println("seis"); break;
     case "7":    
     case "jul":  System.out.println("seite"); break;
     case "8":    
     case "aug":  System.out.println("ocho"); break;
     case "9":   
     case "sep":  System.out.println("nueve"); break;
     case "10":    
     case "oct": System.out.println("diez"); break;
     }
 }

0
投票

我正在开发项目,我需要在我的switch语句中使用break,否则代码将无效。请耐心等待,我将为您提供一个很好的例子,说明您在switch语句中需要break的原因。

想象一下,你有三个状态,一个等待用户输入数字,第二个计算它,第三个打印总和。

在这种情况下,你有:

  1. State1 - 等待用户输入数字
  2. State2 - 打印总和
  3. state3 - 计算总和

查看状态,您希望exaction的顺序从state1开始,然后是state3,最后是state2。否则,我们只会打印用户输入而不计算总和。为了再次澄清它,我们等待用户输入一个值,然后计算总和并打印总和。

这是一个示例代码:

while(1){
    switch(state){
      case state1:
        // Wait for user input code
        state = state3; // Jump to state3
        break;
      case state2:
        //Print the sum code
        state = state3; // Jump to state3;
      case state3:
        // Calculate the sum code
        state = wait; // Jump to state1
        break;
    }
}

如果我们不使用break,它将按此顺序执行,state1,state2和state3。但是使用break,我们避免了这种情况,并且可以在正确的过程中进行排序,该过程以state1开始,然后是state3,最后但最重要的是state2。


-1
投票

确切地说,因为通过一些聪明的放置,您可以级联执行块。


29
投票

Historically,这是因为case基本上定义了label,也被称为target point呼叫的goto。 switch语句及其相关案例实际上只代表一个多路分支,其中有多个潜在的入口点进入代码流。

所有这一切,已经注意到几乎无限次break几乎总是你在每个案例结束时宁愿拥有的默认行为。


27
投票

Java来自C,这是来自C的语法。

有时您希望多个case语句只有一个执行路径。以下是一个示例,它将告诉您一个月中的天数。

class SwitchDemo2 {
    public static void main(String[] args) {

        int month = 2;
        int year = 2000;
        int numDays = 0;

        switch (month) {
            case 1:
            case 3:
            case 5:
            case 7:
            case 8:
            case 10:
            case 12:
                numDays = 31;
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                numDays = 30;
                break;
            case 2:
                if ( ((year % 4 == 0) && !(year % 100 == 0))
                     || (year % 400 == 0) )
                    numDays = 29;
                else
                    numDays = 28;
                break;
            default:
                System.out.println("Invalid month.");
                break;
        }
        System.out.println("Number of Days = " + numDays);
    }
}

13
投票

我认为这是一个错误。作为一种语言结构,将break作为默认值并使用fallthrough关键字变得同样容易。我编写和阅读的大多数代码在每个案例后都有一个中断。


13
投票

案例落后你可以做各种有趣的事情。

例如,假设您想要针对所有情况执行特定操作,但在某些情况下,您希望执行该操作以及其他操作。使用带有连接的switch语句会非常容易。

switch (someValue)
{
    case extendedActionValue:
        // do extended action here, falls through to normal action
    case normalActionValue:
    case otherNormalActionValue:
        // do normal action here
        break;
}

当然,在案件结束时很容易忘记break声明并导致意外行为。当你省略break语句时,好的编译器会警告你。


6
投票

为什么编译器不会在交换机中的每个代码块之后自动放置break语句?

撇开能够在几个案例中使用相同块的良好愿望(可能是特殊情况)......

这是出于历史原因吗?您何时需要执行多个代码块?

这主要是为了与C兼容,并且可以说是当goto关键词漫游地球时的古代黑客。当然,它确实能够实现一些令人惊奇的东西,例如Duff's Device,但无论是对它有利还是反对,它都是......最好的论证。


4
投票

因此,如果您需要多个案例来执行相同的操作,则不必重复代码:

case THIS:
case THAT:
{
    code;
    break;
}

或者你可以做以下事情:

case THIS:
{
   do this;
}
case THAT:
{
   do that;
}

以级联的方式。

如果你问我,真的很容易出错/混乱。


3
投票

Java源自C,其遗产包括称为Duff's Device的技术。这是一个优化,它依赖于在没有break;语句的情况下控制从一个案例落到下一个案例的事实。在C标准化的时候,有很多代码就像“野外”一样,改变语言以打破这种结构会适得其反。


3
投票

就历史记录而言,Tony Hoare在20世纪60年代的“结构化编程”革命中发明了案例陈述。 Tony的案例陈述支持每个案例多个标签和自动退出,没有发臭的break陈述。明确的break的要求来自BCPL / B / C线。 Dennis Ritchie写道(在ACM HOPL-II中):

例如,当我们在20世纪60年代学习它时,从BCPL switchon语句中转义的endcase不存在于语言中,因此break关键字的重载以逃避B和C切换语句归因于不同的进化而不是有意识的更改。

我没有找到任何关于BCPL的历史着作,但是里奇的评论表明,break或多或少是历史性事故。 BCPL后来解决了这个问题,但也许Ritchie和Thompson太忙于发明Unix而烦恼这样的细节:-)

© www.soinside.com 2019 - 2024. All rights reserved.