Mengapa kita perlu istirahat setelah pernyataan kasus?

94

Mengapa kompilator tidak secara otomatis meletakkan pernyataan break setelah setiap blok kode di sakelar? Apakah karena alasan sejarah? Kapan Anda ingin menjalankan beberapa blok kode?

unj2
sumber
2
Membuat jawaban tentang JDK-12 dan label switch direformasi menjadi bukan mandat break.
Naman

Jawaban:

94

Terkadang ada gunanya memiliki beberapa kasus yang terkait dengan blok kode yang sama, seperti

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

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

dll Sekadar contoh.

Dalam pengalaman saya, biasanya itu adalah gaya yang buruk untuk "gagal" dan memiliki beberapa blok kode yang dieksekusi untuk satu kasus, tetapi mungkin ada kegunaannya dalam beberapa situasi.

WildCrustacea
sumber
29
Selalu tambahkan komentar di sepanjang baris // Intentional fallthrough.saat Anda menghilangkan jeda. Menurut pendapat saya, ini bukanlah gaya yang buruk seperti "mudah melupakan istirahat secara tidak sengaja". PS Tentu saja tidak dalam kasus-kasus sederhana seperti pada jawaban itu sendiri.
doublep
@ Doublep - Saya setuju. Menurut pendapat saya, saya akan menghindarinya jika memungkinkan tetapi jika masuk akal maka pastikan sangat jelas apa yang Anda lakukan.
WildCrustacean
6
@ Doublep: Saya tidak akan repot-repot dengan komentar jika kelipatannya caseditumpuk seperti itu. Jika ada kode di antara mereka maka ya, komentar tersebut mungkin pantas.
Billy ONeal
4
Saya membayangkan bahasa di mana Anda dapat mendeklarasikan beberapa kasus dalam satu case, seperti case 'A','B','C': doSomething(); case 'D','E': doSomethingElse();:, tanpa perlu jeda antar kasus. Pascal dapat melakukannya: "Pernyataan kasus membandingkan nilai ekspresi ordinal dengan setiap pemilih, yang dapat berupa konstanta, subrentang, atau daftar di antaranya yang dipisahkan dengan koma." ( wiki.freepascal.org/Case )
Christian Semrau
32

Secara historis , itu karena casepada dasarnya mendefinisikan label, juga dikenal sebagai titik sasaran dari gotopanggilan. Pernyataan switch dan kasus terkaitnya benar-benar hanya mewakili cabang multiway dengan beberapa titik masuk potensial ke dalam aliran kode.

Semua yang dikatakan, telah dicatat dalam jumlah yang hampir tak terbatas yang breakhampir selalu merupakan perilaku default yang Anda inginkan di akhir setiap kasus.

Bob Cross
sumber
30

Java berasal dari C dan itu adalah sintaks dari C.

Ada kalanya Anda ingin beberapa pernyataan kasus hanya memiliki satu jalur eksekusi. Di bawah ini adalah contoh yang akan memberi tahu Anda berapa hari dalam sebulan.

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);
    }
}
Romain Hippeau
sumber
4
Seseorang membidik panah atas dan meleset? Atau mungkin mereka punya daging sapi dengan gaya penjepit atau lekukan Anda ...
Jim Lewis
Entahlah, jadi dapatkan +1 dari saya. Ini adalah contoh di mana fall-through membantu, meskipun saya benar-benar berharap Java memilih pernyataan kasus yang lebih modern. Cobol's EVALUATE-WHEN-OTHERWISE jauh lebih kuat, dan mendahului Java. Ekspresi kecocokan Scala adalah contoh modern tentang apa yang bisa dilakukan.
Jim Ferrans
1
Murid-murid saya akan dicambuk di depan umum karena melakukan ini. Itu anjing hutan jelek.
ncmathsadist
2
@ncmathsadist Ini mengilustrasikan suatu hal dalam satu cara melakukan sesuatu. Saya tidak setuju bahwa contoh ini mungkin ekstrim. Tapi ini adalah contoh dunia nyata, yang saya percaya membantu orang memahami sebuah konsep.
Romain Hippeau
15

Saya pikir itu adalah kesalahan. Sebagai konstruksi bahasa, ini semudah memiliki breakdefault dan sebagai gantinya memiliki fallthroughkata kunci. Sebagian besar kode yang telah saya tulis dan baca memiliki jeda setelah setiap kasus.

Presiden James K. Polk
sumber
4
Saya lebih suka menyarankan continue <case name>yang memungkinkan untuk secara eksplisit menentukan dengan pernyataan kasus mana yang akan dilanjutkan;
Vilx-
4
@Vilx Ketika mengizinkan sembarang casedalam arus switch, ini hanya menjadi goto. ;-)
Christian Semrau
13

Anda dapat melakukan segala macam hal menarik dengan kasus fall-through.

Misalnya, katakanlah Anda ingin melakukan tindakan tertentu untuk semua kasus, tetapi dalam kasus tertentu Anda ingin melakukan tindakan itu ditambah sesuatu yang lain. Menggunakan pernyataan switch dengan fall-through akan membuatnya cukup mudah.

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

Tentu saja, mudah untuk melupakan breakpernyataan di akhir kasus dan menyebabkan perilaku yang tidak terduga. Kompiler yang baik akan memperingatkan Anda saat Anda menghilangkan pernyataan break.

Zach Johnson
sumber
Bisakah switch / case digunakan pada string di Java?
Steve Kuo
@ Steve: Ups, saya rasa tidak untuk saat ini. Menurut stackoverflow.com/questions/338206/… , string akan diizinkan di versi Java yang akan datang. (Saat ini saya melakukan sebagian besar pemrograman saya di C #, yang memungkinkan string dalam pernyataan sakelar.) Saya telah mengedit jawaban untuk menghapus kutipan yang menyesatkan.
Zach Johnson
2
@ZachJohnson, lama kemudian, Java 7 mengizinkan pengaktifan Strings.
Bob Cross
7

Mengapa kompilator tidak secara otomatis meletakkan pernyataan break setelah setiap blok kode di sakelar?

Mengesampingkan keinginan baik untuk dapat menggunakan blok yang sama untuk beberapa kasus (yang dapat berupa kasing khusus) ...

Apakah karena alasan sejarah? Kapan Anda ingin menjalankan beberapa blok kode?

Ini terutama untuk kompatibilitas dengan C, dan bisa dibilang merupakan peretasan kuno dari masa lalu ketika gotokata kunci menjelajahi bumi. Itu memang memungkinkan beberapa hal luar biasa, tentu saja, seperti Perangkat Duff , tetapi apakah itu poin yang menguntungkan atau menentang adalah ... paling banter argumentatif.

Donal Fellows
sumber
5

The breaksetelah saklar cases digunakan untuk menghindari fallthrough dalam laporan switch. Menariknya, hal ini sekarang dapat dicapai melalui label sakelar yang baru dibentuk seperti yang diterapkan melalui JEP-325 .

Dengan perubahan ini, breakdengan setiap sakelar casedapat dihindari seperti yang ditunjukkan lebih lanjut: -

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

Saat menjalankan kode di atas dengan JDK-12 , output komparatif dapat dilihat sebagai

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

dan

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

dan tentu saja hal itu tidak berubah

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

Jadi Anda tidak perlu mengulang kode jika Anda membutuhkan beberapa kasus untuk melakukan hal yang sama:

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

Atau Anda dapat melakukan hal-hal seperti:

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

Dengan gaya bertingkat.

Benar-benar rentan bug / kebingungan, jika Anda bertanya kepada saya.

Francisco Soto
sumber
apakah itu berjalan baik do thisdan do thatuntuk ini tetapi hanya do thatuntuk itu?
JonnyRaa
1
baca saja dokumentasinya. Itu sangat buruk! Sungguh cara yang mudah untuk menulis bug!
JonnyRaa
4

Sejauh catatan sejarah berjalan, Tony Hoare menemukan pernyataan kasus pada tahun 1960-an, selama revolusi "pemrograman terstruktur". Pernyataan kasus Tony mendukung banyak label per casing dan keluar otomatis tanpa breakpernyataan yang tidak sedap . Persyaratan untuk eksplisit breakadalah sesuatu yang keluar dari garis BCPL / B / C. Dennis Ritchie menulis (dalam ACM HOPL-II):

Misalnya, endcase yang keluar dari pernyataan switchon BCPL tidak ada dalam bahasa tersebut ketika kita mempelajarinya di tahun 1960-an, dan overloading kata kunci break untuk keluar dari pernyataan switch B dan C disebabkan oleh evolusi yang berbeda daripada sadar. perubahan.

Saya belum dapat menemukan tulisan sejarah tentang BCPL, tetapi komentar Ritchie menunjukkan bahwa breakitu kurang lebih merupakan kecelakaan sejarah. BCPL kemudian memperbaiki masalah tersebut, tetapi mungkin Ritchie dan Thompson terlalu sibuk menciptakan Unix untuk direpotkan dengan detail seperti itu :-)

Norman Ramsey
sumber
Ini harus mendapatkan lebih banyak suara. Rupanya OP sudah tahu bahwa menghilangkan breakmemungkinkan "beberapa blok kode untuk dieksekusi", dan lebih mementingkan motivasi pilihan desain ini. Yang lain menyebutkan warisan terkenal dari C hingga Jawa, dan jawaban ini mendorong penelitian lebih jauh ke hari-hari sebelum C. Saya berharap kami memiliki pencocokan pola ini (meskipun sangat primitif) sejak awal.
wlnirvana
3

Java diturunkan dari C, yang warisannya mencakup teknik yang dikenal sebagai Perangkat Duff . Ini adalah pengoptimalan yang bergantung pada fakta bahwa kontrol berpindah dari satu kasus ke kasus berikutnya, jika tidak ada break;pernyataan. Pada saat C distandarisasi, ada banyak kode seperti itu "di alam liar", dan akan menjadi kontraproduktif jika mengubah bahasa untuk mematahkan konstruksi semacam itu.

Jim Lewis
sumber
2

Seperti yang orang katakan sebelumnya, itu adalah untuk membiarkan jatuh dan itu bukan kesalahan, itu adalah fitur. Jika terlalu banyak breakpernyataan mengganggu Anda, Anda dapat dengan mudah menyingkirkannya dengan menggunakan returnpernyataan. Ini sebenarnya adalah praktik yang baik, karena metode Anda harus sekecil mungkin (demi keterbacaan dan pemeliharaan), jadi switchpernyataan sudah cukup besar untuk sebuah metode, oleh karena itu, metode yang baik tidak boleh berisi yang lain, ini adalah sebuah contoh:

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

Eksekusi mencetak:

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

seperti yang diharapkan.

Víctor Gil
sumber
0

Tidak adanya jeda otomatis yang ditambahkan oleh kompilator memungkinkan untuk menggunakan sakelar / kasing untuk menguji kondisi seperti 1 <= a <= 3dengan menghapus pernyataan putus dari 1 dan 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;
}
Soufiane Hassou
sumber
Yecch. Aku sangat benci ini.
ncmathsadist
0

karena ada situasi di mana Anda ingin mengalir melalui blok pertama misalnya untuk menghindari penulisan kode yang sama di beberapa blok tetapi masih dapat membaginya untuk lebih banyak kontrol. Ada juga banyak alasan lain.

Jonas B
sumber
0

Ini adalah pertanyaan lama tetapi sebenarnya saya menggunakan case tanpa pernyataan istirahat hari ini. Tidak menggunakan break sebenarnya sangat berguna saat Anda perlu menggabungkan fungsi yang berbeda secara berurutan.

misalnya menggunakan kode respons http untuk mengotentikasi pengguna dengan token waktu

kode respons server 401 - token sudah usang -> buat ulang token dan masukkan pengguna.
Kode respons server 200 - token OK -> masukkan pengguna.

dalam pernyataan kasus:

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

Dengan menggunakan ini, Anda tidak perlu memanggil fungsi pengguna log in untuk respons 401 karena ketika token dibuat ulang, runtime melompat ke kasus 200.

Vladimír Gašpar
sumber
0

Anda dapat dengan mudah memisahkan jenis lain dari angka, bulan, hitungan.
Ini lebih baik jika dalam kasus ini;

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;
     }
 }
John Dev
sumber
0

Saya sekarang mengerjakan proyek di mana saya membutuhkan breakdalam pernyataan switch saya jika tidak kode tidak akan berfungsi. Bersabarlah dengan saya dan saya akan memberi Anda contoh yang baik tentang mengapa Anda membutuhkanbreak dalam pernyataan sakelar Anda.

Bayangkan Anda memiliki tiga status, satu yang menunggu pengguna memasukkan angka, yang kedua untuk menghitungnya dan yang ketiga untuk mencetak jumlahnya.

Jika demikian, Anda memiliki:

  1. State1 - Tunggu pengguna memasukkan nomor
  2. State2 - Cetak jumlahnya
  3. state3 - Hitung jumlahnya

Melihat negara bagian, Anda ingin urutan eksaksi dimulai di state1 , lalu state3 dan terakhir state2 . Jika tidak, kami hanya akan mencetak input pengguna tanpa menghitung jumlahnya. Untuk memperjelasnya lagi, kita menunggu pengguna memasukkan nilai, lalu menghitung jumlahnya dan mencetak jumlahnya.

Berikut adalah contoh kode:

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

Jika kita tidak menggunakannya break, itu akan dieksekusi dalam urutan ini, state1 , state2 dan state3 . Tetapi dengan menggunakan break, kami menghindari skenario ini, dan dapat memesan dalam prosedur yang benar yaitu dimulai dengan state1, kemudian state3 dan terakhir tetapi tidak sedikit state2.

Dler
sumber
-1

Tepatnya, karena dengan beberapa penempatan yang cerdik, Anda dapat mengeksekusi blok secara bertingkat.

Hellektor
sumber