왜 우리는 사건 진술 후 휴식이 필요합니까?


94

컴파일러가 스위치의 각 코드 블록 뒤에 자동으로 break 문을 넣지 않는 이유는 무엇입니까? 역사적 이유 때문입니까? 여러 코드 블록을 언제 실행하고 싶습니까?


2
JDK-12에 대한 답변을 작성 하고 레이블을 변경하여 의무화하지 않았습니다 break.
Naman

답변:


94

때로는 동일한 코드 블록과 관련된 여러 사례를 갖는 것이 도움이됩니다.

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

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

등. 단지 예입니다.

내 경험상 일반적으로 "넘어가는"스타일이 좋지 않고 한 경우에 여러 개의 코드 블록을 실행하는 것이 좋지만 상황에 따라 사용할 수도 있습니다.


28
// Intentional fallthrough.중단을 생략 할 때는 항상 줄을 따라 주석을 추가하십시오 . 제 생각에는 "실수로 휴식을 잊기 쉬운"것만 큼 나쁜 스타일은 아닙니다. 추신 물론 대답 자체처럼 간단한 경우는 아닙니다.
2010

@doublep-동의합니다. 제 생각에는 가능하면 피하고 싶지만 말이된다면 무엇을하고 있는지 매우 명확하게 확인하십시오.
WildCrustacean 2010

6
@doublep : 여러 cases가 그런 식으로 쌓이면 주석을 다룰 필요가 없습니다 . 그들 사이에 코드가 있으면 예, 주석은 아마도 가치가 있습니다.
Billy ONeal

4
나는 당신이 하나 case에서 여러 케이스를 선언 할 수있는 언어를 상상합니다 case 'A','B','C': doSomething(); case 'D','E': doSomethingElse();. Pascal은 다음과 같이 할 수 있습니다. "case 문은 상수, 하위 범위 또는 쉼표로 구분 된 목록 일 수있는 각 선택자와 서수 표현식의 값을 비교합니다." ( wiki.freepascal.org/Case )
기독교 Semrau

32

역사적으로 , case는 기본적 으로 호출 label대상 지점 이라고도 하는를 정의 했기 때문 goto입니다. switch 문과 관련 사례는 실제로 코드 스트림에 대한 여러 잠재적 진입 점이있는 다중 분기를 나타냅니다.

즉, break거의 모든 경우가 끝날 때마다 원하는 기본 동작 인 거의 무한한 횟수가 기록되었습니다 .


30

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

4
누군가 위쪽 화살표를 겨냥하고 놓쳤습니까? 아니면 중괄호 스타일이나 들여 쓰기가있는 쇠고기
Jim Lewis

Dunno, 그러니 나 한테 +1 해줘. Java가 더 현대적인 case 문을 선택했으면 좋겠지 만 이것은 폴스 루가 도움이되는 예입니다. Cobol의 EVALUATE-WHEN-OTHERWISE는 훨씬 더 강력하며 Java 보다 앞서 있습니다. Scala의 매치 표현식은 무엇을 할 수 있는지에 대한 현대적인 예입니다.
Jim Ferrans 2010

1
제 학생들은이 일에 대해 공개적으로 채찍질을 당할 것입니다. 코요테 추악합니다.
ncmathsadist 2014 년

2
@ncmathsadist 그것은 어떤 일을하는 한 가지 방법에 대한 요점을 보여줍니다. 나는이 예가 아마도 극단적 일 것이라는 데 동의하지 않습니다. 그러나 이것은 사람들이 개념을 이해하는 데 도움이되는 실제 사례입니다.
Romain Hippeau 2014 년

15

실수라고 생각합니다. 언어 구조 break로서 기본값 만큼이나 쉽게 fallthrough키워드 를 가지고 있습니다 . 내가 작성하고 읽은 대부분의 코드는 모든 경우마다 중단됩니다.


4
차라리 continue <case name>어떤 case 문을 계속할 것인지 명시 적으로 지정할 수있는 방법을 제안하고 싶습니다 .
Vilx-

4
@Vilx case현재 내에서 임의의 것을 허용 할 때 switch이것은 단순히 goto. ;-)
Christian Semrau

13

케이스 폴스 루로 모든 종류의 흥미로운 일을 할 수 있습니다.

예를 들어 모든 경우에 특정 작업을 수행하고 싶지만 특정 경우에는 해당 작업과 다른 작업을 수행하고 싶다고 가정 해 보겠습니다. fall-through와 함께 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 문을 생략하면 경고합니다.


Java의 문자열에 스위치 / 케이스를 사용할 수 있습니까?
Steve Kuo

@Steve : 죄송합니다. 지금은 아닙니다. stackoverflow.com/questions/338206/… 에 따르면 , Java의 차기 버전에서는 문자열이 허용됩니다. (저는 현재 대부분의 프로그래밍을 C #에서 수행하며, 이는 switch 문에 문자열을 허용합니다.) 오해의 소지가있는 따옴표를 제거하기 위해 대답을 편집했습니다.
Zach Johnson

2
@ZachJohnson, 훨씬 나중에 Java 7은 문자열 전환을 허용합니다.
Bob Cross

7

컴파일러가 스위치의 각 코드 블록 뒤에 자동으로 break 문을 넣지 않는 이유는 무엇입니까?

여러 경우 (특별한 경우 일 수 있음)에 대해 동일한 블록을 사용할 수 있다는 좋은 욕망을 제쳐두고 ...

역사적 이유 때문입니까? 여러 코드 블록을 언제 실행하고 싶습니까?

주로 C와의 호환성을위한 것이며 goto키워드가 지구를 돌아 다니던 옛날부터의 고대 해킹 일 것입니다 . 그것은 않습니다 같은 물론 몇 가지 놀라운 일들 활성화 더프의 장치를 하지만, 그 여부는 유리한 점은 나입니다 ... 논란의 여지가 최선을 상대로.


5

break후 스위치 case들 스위치 문에서 위해 fallthrough을 방지하기 위해 사용된다. 흥미롭게도 JEP-325 를 통해 구현 된 새로 형성된 스위치 레이블을 통해이 작업을 수행 할 수 있습니다 .

이러한 변경 사항으로 인해 추가 시연 된대로 break모든 스위치 case를 사용하지 않아도됩니다.

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");
        }
    }
}

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

4

따라서 동일한 작업을 수행하기 위해 여러 경우가 필요한 경우 코드를 반복 할 필요가 없습니다.

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

또는 다음과 같은 작업을 수행 할 수 있습니다.

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

캐스케이드 방식으로.

저에게 물어 보면 버그 / 혼란이 발생하기 쉽습니다.


그 실행을 모두 수행 do this하고 do that이를 위해 그러나 다만 do that그것을 위해?
JonnyRaa 2014-08-27

1
문서를 읽으십시오. 끔찍 해요! 버그를 작성하는 얼마나 쉬운 방법입니까!
JonnyRaa 2014-08-27

4

역사적 기록에 관한 한 Tony Hoare는 1960 년대 "구조화 된 프로그래밍"혁명 기간 동안 사례 진술을 발명했습니다. Tony의 case 문은 케이스 당 여러 레이블을 지원하고 악취가 나는 break문 이없는 자동 종료를 지원했습니다 . 명시 적 요구 사항 break은 BCPL / B / C 라인에서 나온 것입니다. Dennis Ritchie는 다음과 같이 씁니다 (ACM HOPL-II에서).

예를 들어, BCPL switchon 문에서 이스케이프하는 endcase는 1960 년대에 배웠을 때 언어에 존재하지 않았으므로 B 및 C switch 문에서 탈출하기 위해 break 키워드를 오버로딩 한 것은 의식이 아닌 진화의 발산 때문입니다. 변화.

BCPL에 대한 역사적 저작을 찾을 수 없었지만 Ritchie의 의견은 그것이 break다소 역사적 사고 였다는 것을 암시합니다 . BCPL은 나중에 문제를 해결했지만 아마도 Ritchie와 Thompson은 그러한 세부 사항을 신경 쓰기에는 유닉스를 발명하는데 너무 바빴습니다. :-)


이것은 더 많은 표를 얻을 것입니다. 분명히 OP는 생략이 break"다중 코드 블록 실행"을 허용 한다는 것을 이미 알고 있으며이 설계 선택의 동기에 더 관심이 있습니다. 다른 사람들은 C에서 Java에 이르기까지 잘 알려진 유산을 언급했으며이 답변은 연구를 C 이전 시대로 훨씬 더 밀어 붙였습니다. 나는 우리가 처음부터이 (아주 원시적이지만) 패턴 매칭이 있었으면 좋겠다.
wlnirvana

3

Java는 C에서 파생되었으며 그 유산에는 Duff 's Device 라는 기술이 포함되어 있습니다. 이는 break;명령문 이없는 경우 제어가 한 사례에서 다음 사례로 넘어 간다는 사실에 의존하는 최적화입니다 . C가 표준화 될 무렵에는 "야생"과 같은 코드가 많았으며 이러한 구조를 깨기 위해 언어를 변경하는 것은 비생산적이었을 것입니다.


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 <= a <= 31과 2에서 break 문을 제거하는 것과 같은 조건을 테스트 할 수 있습니다 .

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;
}

Yecch. 나는 이것을 완전히 싫어한다.
ncmathsadist 2014 년

0

예를 들어 여러 블록에 동일한 코드를 작성하는 것을 방지하기 위해 첫 번째 블록을 통과하려는 상황이 있지만 여전히 mroe 제어를 위해 분할 할 수 있기 때문입니다. 다른 많은 이유도 있습니다.


0

오래된 질문이지만 실제로는 오늘 break 문없이 case를 사용했습니다. 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;
        }

이것을 사용하면 토큰이 재생성 될 때 런타임이 케이스 200으로 점프하기 때문에 401 응답에 대해 로그인 사용자 함수를 호출 할 필요가 없습니다.


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

나는 지금 break내 switch 문 에서 필요한 프로젝트에서 작업 중입니다. 그렇지 않으면 코드가 작동하지 않습니다. 저를 참아 break주시면 switch 문 에 왜 필요한지 좋은 예를 들어 드리겠습니다 .

사용자가 숫자를 입력 할 때까지 대기하는 상태,이를 계산하는 두 번째 상태, 합계를 인쇄하는 세 번째 상태의 세 가지 상태가 있다고 가정 해보십시오.

이 경우 다음이 있습니다.

  1. State1- 사용자가 숫자를 입력 할 때까지 기다 립니다.
  2. State2- 합계 인쇄
  3. state3- 합계 계산

상태를 보면, 당신은 수렴의 순서에 시작하려는 것입니다 상태 1 다음, 상태 3 마지막 상태 2 . 그렇지 않으면 합계를 계산하지 않고 사용자 입력 만 인쇄합니다. 다시 명확히하기 위해 사용자가 값을 입력 할 때까지 기다린 다음 합계를 계산하고 합계를 인쇄합니다.

다음은 예제 코드입니다.

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,이 순서에 실행됩니다 상태 1 , 상태 2상태 3 . 그러나를 사용 break하면이 시나리오를 피하고 state1, state3, 마지막으로 state2로 시작하는 올바른 절차를 주문할 수 있습니다.


-1

정확합니다. 몇 가지 영리한 배치를 사용하면 계단식으로 블록을 실행할 수 있기 때문입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.