Tại sao chúng ta cần các câu lệnh break sau case?


Câu trả lời:


94

Đôi khi sẽ hữu ích nếu có nhiều trường hợp được liên kết với cùng một khối mã, chẳng hạn như

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

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

vv Chỉ là một ví dụ.

Theo kinh nghiệm của tôi, thường thì kiểu "lọt thỏm" và có nhiều khối mã được thực thi cho một trường hợp là một phong cách tồi, nhưng có thể có cách sử dụng cho nó trong một số trường hợp.


28
Chỉ cần luôn thêm nhận xét dọc theo dòng // Intentional fallthrough.khi bạn bỏ qua dấu ngắt. Theo quan điểm của tôi, đó không phải là một phong cách tồi tệ vì "dễ quên một lần nghỉ ngơi". PS Tất nhiên không phải trong những trường hợp đơn giản như trong câu trả lời của chính nó.
doublep

@doublep - Tôi đồng ý. Theo ý kiến ​​của tôi, tôi sẽ tránh nó nếu có thể nhưng nếu nó có ý nghĩa thì hãy đảm bảo rằng bạn đang làm rất rõ ràng.
WildCrustacean

6
@doublep: Tôi sẽ không bận tâm đến nhận xét nếu nhiều cases được xếp chồng lên nhau theo cách đó. Nếu có mã ở giữa chúng thì có, nhận xét có thể là xứng đáng.
Billy ONeal

4
Tôi tưởng tượng một ngôn ngữ mà bạn có thể khai báo nhiều trường hợp trong một case, như sau case 'A','B','C': doSomething(); case 'D','E': doSomethingElse();:, mà không cần ngắt giữa các trường hợp. Pascal có thể làm điều đó: "Câu lệnh trường hợp so sánh giá trị của biểu thức thứ tự với mỗi bộ chọn, có thể là một hằng số, một dãy con hoặc một danh sách chúng được phân tách bằng dấu phẩy." ( wiki.freepascal.org/Case )
Christian Semrau

32

Về mặt lịch sử , đó là vì casecơ bản xác định a label, còn được gọi là điểm mục tiêu củagoto cuộc gọi. Câu lệnh switch và các trường hợp liên quan của nó thực sự chỉ đại diện cho một nhánh multiway với nhiều điểm nhập tiềm năng vào một dòng mã.

Tất cả những gì đã nói, nó đã được ghi nhận gần như vô hạn lần breakgần như luôn luôn là hành vi mặc định mà bạn muốn có ở cuối mọi trường hợp.


30

Java đến từ C và đó là cú pháp từ C.

Đôi khi bạn muốn nhiều câu lệnh trường hợp chỉ có một đường dẫn thực thi. Dưới đây là một mẫu sẽ cho bạn biết có bao nhiêu ngày trong một tháng.

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
Ai đó nhắm vào mũi tên lên và bắn trượt? Hoặc có thể họ đã có một miếng thịt bò với phong cách nẹp hoặc thụt vào của bạn ...
Jim Lewis

Dunno, vì vậy hãy có +1 từ tôi. Đây là một ví dụ trong đó việc chuyển đổi có ích, mặc dù tôi thực sự ước Java đã chọn một câu lệnh trường hợp hiện đại hơn. ĐÁNH GIÁ KHI NÀO KHÁC CỦA Cobol mạnh hơn nhiều và có trước Java. Biểu thức đối sánh của Scala là một ví dụ hiện đại về những gì có thể được thực hiện.
Jim Ferrans

1
Học sinh của tôi sẽ bị đánh giá công khai vì làm điều này. Nó xấu xí.
ncmathsadist

2
@ncmathsadist Nó minh họa một điểm về một cách thực hiện điều gì đó. Tôi không đồng ý rằng ví dụ này có lẽ là cực đoan. Nhưng đó là một ví dụ thực tế, mà tôi tin rằng sẽ giúp mọi người hiểu được một khái niệm.
Romain Hippeau

15

Tôi nghĩ đó là một sai lầm. Là một ngôn ngữ xây dựng, nó dễ dàng breaknhư mặc định và thay vào đó có một fallthroughtừ khóa. Hầu hết các mã tôi đã viết và đọc đều có khoảng nghỉ sau mỗi trường hợp.


4
Tôi muốn đề xuất continue <case name>cái nào cho phép chỉ định rõ ràng câu lệnh trường hợp nào để tiếp tục;
Vilx-

4
@Vilx Khi cho phép một tùy chọn casetrong dòng điện switch, điều này đơn giản trở thành một goto. ;-)
Christian Semrau

13

Bạn có thể làm tất cả những điều thú vị với trường hợp dự phòng.

Ví dụ: giả sử bạn muốn thực hiện một hành động cụ thể cho tất cả các trường hợp, nhưng trong một trường hợp nhất định, bạn muốn thực hiện hành động đó cùng với hành động khác. Sử dụng một câu lệnh switch với Fall-through sẽ làm cho nó khá dễ dàng.

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

Tất nhiên, bạn rất dễ quên breaklời khai khi kết thúc vụ án và gây ra những hành vi không mong muốn. Trình biên dịch tốt sẽ cảnh báo bạn khi bạn bỏ qua câu lệnh break.


Có thể sử dụng switch / case trên chuỗi trong Java không?
Steve Kuo

@Steve: Rất tiếc, tôi đoán không phải lúc này. Theo stackoverflow.com/questions/338206/… , chuỗi sẽ được phép trong phiên bản Java trong tương lai. (Tôi hiện đang làm hầu hết các lập trình của mình bằng C #, điều này cho phép các chuỗi trong câu lệnh switch.) Tôi đã chỉnh sửa câu trả lời để loại bỏ các dấu ngoặc kép gây hiểu lầm.
Zach Johnson

2
@ZachJohnson, sau này, Java 7 cho phép chuyển đổi trên Chuỗi.
Bob Cross

7

Tại sao trình biên dịch không tự động đặt các câu lệnh ngắt sau mỗi khối mã trong công tắc?

Bỏ mong muốn tốt đẹp là có thể sử dụng khối giống hệt nhau cho một số trường hợp (có thể được đặt chữ đặc biệt) ...

Có phải vì lý do lịch sử? Khi nào bạn muốn nhiều khối mã thực thi?

Nó chủ yếu để tương thích với C, và được cho là một cách hack cổ xưa từ những ngày xa xưa khi gotocác từ khóa lan rộng trên trái đất. Tất nhiên, cho phép một số thứ tuyệt vời, chẳng hạn như Thiết bị của Duff , nhưng cho dù đó là điểm có lợi hay chống lại thì… tốt nhất là tranh luận.


5

Sau breakkhi chuyển đổi cases được sử dụng để tránh sự nhầm lẫn trong các câu lệnh chuyển đổi. Mặc dù điều thú vị là bây giờ có thể đạt được điều này thông qua các nhãn chuyển đổi mới được hình thành như được triển khai qua JEP-325 .

Với những thay đổi này, có thể tránh được breakvới mọi công tắc casenhư được trình bày thêm: -

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

Khi thực thi đoạn mã trên với JDK-12 , kết quả so sánh có thể được xem là

//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

và tất nhiên mọi thứ không thay đổi

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

4

Vì vậy, bạn không phải lặp lại mã nếu bạn cần một số trường hợp để làm điều tương tự:

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

Hoặc bạn có thể làm những việc như:

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

Trong một thời trang thác.

Thực sự dễ bị lỗi / nhầm lẫn, nếu bạn hỏi tôi.


không chạy cả hai do thisdo thatcho cái này nhưng chỉ do thatcho cái kia?
JonnyRaa

1
chỉ cần đọc tài liệu. Cái đó ghê thật! Thật là một cách dễ dàng để viết lỗi!
JonnyRaa

4

Theo như ghi chép lịch sử, Tony Hoare đã phát minh ra câu lệnh tình huống vào những năm 1960, trong cuộc cách mạng "lập trình có cấu trúc". Báo cáo trường hợp của Tony hỗ trợ nhiều nhãn cho mỗi trường hợp và tự động thoát mà không có breakcâu lệnh hôi thối . Yêu cầu rõ ràng breaklà một cái gì đó xuất phát từ dòng BCPL / B / C. Dennis Ritchie viết (trong ACM HOPL-II):

Ví dụ: endcase thoát khỏi câu lệnh BCPL switchhon không có trong ngôn ngữ khi chúng ta học nó vào những năm 1960, và do đó, việc quá tải từ khóa break để thoát khỏi câu lệnh switch B và C là do sự tiến hóa khác nhau thay vì có ý thức. thay đổi.

Tôi không thể tìm thấy bất kỳ bài viết lịch sử nào về BCPL, nhưng bình luận của Ritchie cho thấy rằng đó breakít nhiều là một tai nạn lịch sử. BCPL sau đó đã khắc phục được sự cố, nhưng có lẽ Ritchie và Thompson quá bận rộn với việc phát minh ra Unix nên mới bận tâm đến một chi tiết như vậy :-)


Điều này sẽ nhận được nhiều phiếu bầu hơn. Rõ ràng OP đã biết việc bỏ qua breakcho phép "nhiều khối mã thực thi", và quan tâm hơn đến động cơ của lựa chọn thiết kế này. Những người khác đề cập đến di sản nổi tiếng từ C sang Java, và câu trả lời này đã thúc đẩy nghiên cứu tiến xa hơn đến những ngày trước C. Tôi ước chúng ta có mẫu này (mặc dù rất sơ khai) phù hợp ngay từ đầu.
wlnirvana

3

Java có nguồn gốc từ C, có di sản bao gồm một kỹ thuật được gọi là Thiết bị của Duff . Đó là một sự tối ưu hóa dựa trên thực tế là quyền kiểm soát được thực hiện từ trường hợp này sang trường hợp khác mà không cần break;tuyên bố. Vào thời điểm C được chuẩn hóa, có rất nhiều đoạn mã như thế "trong tự nhiên", và việc thay đổi ngôn ngữ để phá vỡ các cấu trúc như vậy sẽ phản tác dụng.


1

Như mọi người đã nói trước đây, nó là để cho phép rơi và nó không phải là một sai lầm, nó là một tính năng. Nếu quá nhiều breakcâu lệnh làm phiền bạn, bạn có thể dễ dàng loại bỏ chúng bằng cách sử dụng returncâu lệnh thay thế. Đây thực sự là một cách thực hành tốt, bởi vì các phương thức của bạn phải càng nhỏ càng tốt (vì lợi ích của việc dễ đọc và dễ bảo trì), vì vậy một switchcâu lệnh đã đủ lớn cho một phương thức, do đó, một phương thức tốt không nên chứa bất kỳ thứ gì khác, đây là một ví dụ:

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

Bản in thực thi:

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

như mong đợi.


0

Việc trình biên dịch không thêm ngắt tự động làm cho nó có thể sử dụng một công tắc / trường hợp để kiểm tra các điều kiện như 1 <= a <= 3bằng cách loại bỏ câu lệnh ngắt từ 1 và 2.

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. Tôi hoàn toàn ghét điều này.
ncmathsadist

0

bởi vì có những tình huống mà bạn muốn chuyển qua khối đầu tiên chẳng hạn để tránh viết cùng một mã trong nhiều khối nhưng vẫn có thể chia chúng để kiểm soát mroe. Ngoài ra còn có rất nhiều lý do khác.


0

Đó là một câu hỏi cũ nhưng thực sự hôm nay tôi đã gặp phải trường hợp không có câu lệnh break. Không sử dụng break thực sự rất hữu ích khi bạn cần kết hợp các chức năng khác nhau theo trình tự.

ví dụ: sử dụng mã phản hồi http để xác thực người dùng bằng mã thông báo thời gian

mã phản hồi máy chủ 401 - mã thông báo đã lỗi thời -> tạo mã thông báo và đăng nhập người dùng.
Mã phản hồi của máy chủ 200 - mã thông báo là OK -> đăng nhập người dùng.

trong câu lệnh trường hợp:

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

Sử dụng điều này, bạn không cần phải gọi hàm người dùng đăng nhập cho phản hồi 401 vì khi mã thông báo được tạo lại, thời gian chạy sẽ chuyển sang trường hợp 200.


0

Bạn có thể dễ dàng tách loại số, tháng, số khác.
Điều này tốt hơn sau đó nếu trong trường hợp này;

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

Bây giờ tôi đang làm việc trên dự án mà tôi cần breaktrong câu lệnh chuyển đổi của mình nếu không mã sẽ không hoạt động. Hãy chịu khó và tôi sẽ cung cấp cho bạn một ví dụ điển hình về lý do tại sao bạn cần breaktrong tuyên bố chuyển đổi của mình.

Hãy tưởng tượng bạn có ba trạng thái, một trạng thái chờ người dùng nhập một số, trạng thái thứ hai để tính toán và trạng thái thứ ba để in ra tổng.

Trong trường hợp đó, bạn có:

  1. State1 - Chờ người dùng nhập số
  2. State2 - In tổng
  3. state3 - Tính tổng

Nhìn vào các tiểu bang, bạn sẽ muốn thứ tự của sự đòi thuế để bắt đầu trên state1 , sau đó state3 và cuối cùng state2 . Nếu không, chúng tôi sẽ chỉ in đầu vào của người dùng mà không tính tổng. Để làm rõ một lần nữa, chúng tôi đợi người dùng nhập một giá trị, sau đó tính tổng và in ra tổng.

Đây là một mã ví dụ:

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

Nếu chúng ta không sử dụng break, nó sẽ thực thi theo thứ tự này, state1 , state2state3 . Nhưng bằng cách sử dụng break, chúng ta tránh được trường hợp này và có thể sắp xếp theo đúng quy trình là bắt đầu bằng trạng thái1, sau đó là trạng thái3 và cuối cùng nhưng không kém phần quan trọng nhất là trạng thái2.


-1

Chính xác, bởi vì với một số vị trí thông minh, bạn có thể thực hiện các khối theo tầng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.