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?
Jawaban:
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.
// 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.
case
ditumpuk seperti itu. Jika ada kode di antara mereka maka ya, komentar tersebut mungkin pantas.
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 )
Secara historis , itu karena case
pada dasarnya mendefinisikan label
, juga dikenal sebagai titik sasaran dari goto
panggilan. 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 break
hampir selalu merupakan perilaku default yang Anda inginkan di akhir setiap kasus.
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);
}
}
Saya pikir itu adalah kesalahan. Sebagai konstruksi bahasa, ini semudah memiliki break
default dan sebagai gantinya memiliki fallthrough
kata kunci. Sebagian besar kode yang telah saya tulis dan baca memiliki jeda setelah setiap kasus.
continue <case name>
yang memungkinkan untuk secara eksplisit menentukan dengan pernyataan kasus mana yang akan dilanjutkan;
case
dalam arus switch
, ini hanya menjadi goto
. ;-)
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 break
pernyataan di akhir kasus dan menyebabkan perilaku yang tidak terduga. Kompiler yang baik akan memperingatkan Anda saat Anda menghilangkan pernyataan break.
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 goto
kata 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.
The break
setelah saklar case
s 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, break
dengan setiap sakelar case
dapat 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
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.
do this
dan do that
untuk ini tetapi hanya do that
untuk itu?
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 break
pernyataan yang tidak sedap . Persyaratan untuk eksplisit break
adalah 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 break
itu 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 :-)
break
memungkinkan "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.
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.
Seperti yang orang katakan sebelumnya, itu adalah untuk membiarkan jatuh dan itu bukan kesalahan, itu adalah fitur. Jika terlalu banyak break
pernyataan mengganggu Anda, Anda dapat dengan mudah menyingkirkannya dengan menggunakan return
pernyataan. Ini sebenarnya adalah praktik yang baik, karena metode Anda harus sekecil mungkin (demi keterbacaan dan pemeliharaan), jadi switch
pernyataan 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.
Tidak adanya jeda otomatis yang ditambahkan oleh kompilator memungkinkan untuk menggunakan sakelar / kasing untuk menguji kondisi seperti 1 <= a <= 3
dengan 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;
}
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.
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;
}
}
Saya sekarang mengerjakan proyek di mana saya membutuhkan break
dalam 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:
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.
break
.