Vaka açıklamalarından sonra neden ara vermeye ihtiyacımız var?


94

Derleyici neden anahtardaki her kod bloğundan sonra otomatik olarak break ifadeleri koymuyor? Tarihsel nedenlerle mi? Birden çok kod bloğunun ne zaman yürütülmesini istersiniz?


2
JDK-12 ile ilgili bir cevap verdiler ve etiketleri zorunlu kılmayacak şekilde değiştirildi break.
Naman

Yanıtlar:


94

Bazen aynı kod bloğuyla ilişkili birden fazla vakaya sahip olmak yararlıdır, örneğin

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

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

vb. Sadece bir örnek.

Tecrübelerime göre, genellikle "gözden düşmek" ve bir durum için birden fazla kod bloğunun çalıştırılması kötü bir tarzdır, ancak bazı durumlarda bunun için kullanımları olabilir.


29
Bir // Intentional fallthrough.arayı atladığınızda her zaman satırlara bir yorum ekleyin . Bana göre "kazara bir molayı unutmak kolay" kadar kötü bir tarz değil. Not: Tabii ki cevabın kendisinde olduğu gibi basit durumlarda değil.
04'te doublep

@doublep - Katılıyorum. Benim fikrime göre, mümkünse bundan kaçınırım, ancak mantıklıysa, ne yaptığınızdan çok net emin olun.
WildCrustacean

6
@doublep: Birden çok URL casebu şekilde bir araya getirilirse yorumla uğraşmam . Aralarında kod varsa evet, yorum muhtemelen haklı.
Billy ONeal

4
Birden çok vakayı tek bir vakada açıklayabileceğiniz bir dil hayal ediyorum case, örneğin:, case 'A','B','C': doSomething(); case 'D','E': doSomethingElse();vakalar arasında ara vermeye gerek kalmadan. Pascal bunu yapabilir: "case deyimi, sıralı ifadenin değerini her bir seçici ile karşılaştırır; bu bir sabit, bir alt aralık veya virgülle ayrılmış bir liste olabilir." ( wiki.freepascal.org/Case )
Christian Semrau

32

Tarihsel olarak , bunun nedeni caseesasen bir çağrının hedef noktasılabel olarak da bilinen a'yı tanımlamasıydı . Switch deyimi ve bununla ilişkili durumlar, bir kod akışına birden çok potansiyel giriş noktası olan çok yollu bir dalı temsil eder.goto

Tüm söylenenler, breakneredeyse her zaman her vakanın sonunda sahip olmayı tercih edeceğiniz varsayılan davranış olan neredeyse sonsuz sayıda not edilmiştir .


30

Java, C'den gelir ve bu C'den gelen sözdizimi.

Birden çok durum ifadesinin yalnızca bir yürütme yoluna sahip olmasını istediğiniz zamanlar vardır. Aşağıda, ayda kaç gün olduğunu size söyleyecek bir örnek var.

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
Biri yukarı oku hedefledi ve ıskaladı mı? Ya da belki de ... senin ayracı tarzı veya girinti ile sığır eti vardı
Jim Lewis

Dunno, benden +1 al. Bu, geri dönüşün yardımcı olduğu bir örnektir, ancak gerçekten Java'nın daha modern bir durum ifadesi seçmiş olmasını isterdim. Cobol'un DEĞERLENDİRMESİ-AKSİ ZAMAN çok daha güçlüdür ve Java'dan öncedir . Scala'nın eşleştirme ifadesi, yapılabileceklerin modern bir örneğidir.
Jim Ferrans

1
Öğrencilerim bunu yaptıkları için alenen kırbaçlanırdı. Çakal çirkin.
ncmathsadist

2
@ncmathsadist Bir şeyi yapmanın bir yolu üzerindeki bir noktayı gösterir. Bu örneğin muhtemelen aşırı olduğuna katılmıyorum. Ancak bu, insanların bir kavramı anlamalarına yardımcı olduğuna inandığım gerçek bir dünya örneği.
Romain Hippeau

15

Bunun bir hata olduğunu düşünüyorum. Bir dil yapısı breakolarak, varsayılan olarak sahip olmak ve bunun yerine bir fallthroughanahtar kelimeye sahip olmak kadar kolaydır . Yazdığım ve okuduğum kodların çoğu her durumdan sonra bir ara veriyor.


4
continue <case name>Hangisinin hangi durum ifadesiyle devam edileceğini açıkça belirtmeye izin vermesini öneririm ;
Vilx

4
@Vilx caseAkım içinde keyfi bir duruma izin verirken switch, bu sadece bir goto. ;-)
Christian Semrau

13

Vakaların gözden geçirilmesi ile her türlü ilginç şeyi yapabilirsiniz.

Örneğin, tüm durumlar için belirli bir eylemi yapmak istediğinizi varsayalım, ancak belirli bir durumda bu eylemi artı başka bir şey yapmak istiyorsunuz. Fall-through ile bir switch deyimi kullanmak bunu oldukça kolaylaştıracaktır.

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

Elbette breakbir vaka sonunda ifadeyi unutmak ve beklenmedik davranışlara neden olmak kolaydır . İyi derleyiciler, break ifadesini atladığınızda sizi uyaracaktır.


Java'daki dizelerde switch / case kullanılabilir mi?
Steve Kuo

@Steve: Oops, sanırım şu anda değil. Stackoverflow.com/questions/338206/… adresine göre , dizelere Java'nın gelecekteki bir sürümünde izin verilecektir. (Şu anda programımın çoğunu, anahtar deyimlerinde dizelere izin veren C # ile yapıyorum.) Yanıltıcı alıntıları kaldırmak için yanıtı düzenledim.
Zach Johnson

2
@ZachJohnson, çok daha sonra Java 7, Dizeleri açmaya izin veriyor.
Bob Cross

7

Derleyici neden anahtardaki her kod bloğundan sonra otomatik olarak break ifadeleri koymuyor?

Kenara bırakırsak iyi arzusu (-özel kılıflı olabilir) çeşitli durumlar için aynı blok kullanmak mümkün ...

Tarihsel nedenlerle mi? Birden çok kod bloğunun ne zaman yürütülmesini istersiniz?

Esas olarak C ile uyumluluk içindir ve muhtemelen gotoanahtar kelimelerin dünyayı dolaştığı eski günlerden kalma eski bir hack . Bu does gibi tabii bazı şaşırtıcı şeyler etkinleştirmek, Duff Cihazı , ama bu olsun onun lehine bir nokta veya bir ... tartışmacı en iyi karşı.


5

breakSonra anahtarı cases anahtarı tablolara fallthrough önlemek için kullanılır. İlginç bir şekilde bu artık JEP-325 aracılığıyla uygulanan yeni oluşturulmuş anahtar etiketleri aracılığıyla gerçekleştirilebilir .

Bu değişikliklerle, daha da gösterildiği gibi breakher anahtardan casekaçınılabilir: -

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

Açık JDK-12 ile birlikte yukarıda kod yürütme , Karşılaştırmalı çıkış olarak görülebilir

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

ve

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

ve tabii ki değişmeyen şey

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

4

Dolayısıyla, aynı şeyi yapmak için birkaç duruma ihtiyacınız varsa kodu tekrarlamanız gerekmez:

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

Veya aşağıdaki gibi şeyler yapabilirsiniz:

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

Basamaklı bir tarzda.

Bana sorarsan gerçekten hata / kafa karışıklığı eğilimli.


Bu çalışma hem yapar do thisve do thatbunun için ama sadece do thatbunun için?
JonnyRaa

1
sadece dokümanları okuyun. Bu korkunç! Böcek yazmak için ne kadar kolay bir yol!
JonnyRaa

4

Tarihsel kayıtlara gelince, Tony Hoare 1960'larda "yapılandırılmış programlama" devrimi sırasında vaka açıklamasını icat etti. Tony'nin vaka ifadesi, vaka başına birden fazla etiketi ve kötü kokulu breakifadeler olmadan otomatik çıkışı destekledi . Bir açıklık gerekliliği break, BCPL / B / C hattından gelen bir şeydi. Dennis Ritchie yazıyor (ACM HOPL-II'de):

Örneğin, bir BCPL değiştirme ifadesinden kaçan son durum, 1960'larda öğrendiğimizde dilde mevcut değildi ve bu nedenle, break anahtar kelimesinin B ve C anahtar ifadesinden kaçmak için aşırı yüklenmesi bilinçli olmaktan ziyade farklı evrime borçludur. değişiklik.

BCPL hakkında herhangi bir tarihi yazı bulamadım, ancak Ritchie'nin yorumu, bunun breakaşağı yukarı tarihi bir kaza olduğunu gösteriyor. BCPL daha sonra sorunu çözdü, ancak belki de Ritchie ve Thompson Unix'i icat etmekle çok meşguldüler, böyle bir ayrıntıyla uğraşılmayacak :-)


Bu daha fazla oy almalı. Görünüşe göre OP, ihmal etmenin break"birden fazla kod bloğunun yürütülmesine" izin verdiğini biliyordu ve bu tasarım seçiminin motivasyonuyla daha çok ilgileniyor. Diğerleri C'den Java'ya kadar iyi bilinen mirastan bahsetti ve bu cevap araştırmayı C öncesi günlere daha da itti. Keşke en başından beri bu (çok ilkel olsa da) kalıp eşleşmesine sahip olsaydık.
wlnirvana

3

Java, mirası Duff's Device olarak bilinen bir tekniği içeren C'den türetilmiştir . Bir break;ifade olmadan kontrolün bir vakadan diğerine geçtiği gerçeğine dayanan bir optimizasyon . C standardize edildiğinde, "doğada" böyle bir çok kod vardı ve bu tür yapıları kırmak için dili değiştirmek ters etki yaratabilirdi.


2

İnsanların daha önce söylediği gibi, düşmeye izin vermek ve bu bir hata değil, bir özellik. Çok fazla breakifade sizi rahatsız ediyorsa, returnbunun yerine ifadeler kullanarak bunlardan kolayca kurtulabilirsiniz . Bu aslında iyi bir uygulamadır, çünkü yöntemleriniz olabildiğince küçük olmalıdır (okunabilirlik ve sürdürülebilirlik açısından), bu nedenle bir switchifade zaten bir yöntem için yeterince büyüktür, bu nedenle iyi bir yöntem başka hiçbir şey içermemelidir, bu Bir örnek:

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

Yürütme yazdırır:

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

beklenildiği gibi.


0

Derleyici tarafından otomatik bir break eklenmemiş olması 1 <= a <= 3, break ifadesini 1 ve 2'den kaldırmak gibi koşulları test etmek için bir anahtar / durum kullanmayı mümkün kılar .

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. Bundan kesinlikle nefret ediyorum.
ncmathsadist

0

çünkü, örneğin aynı kodu birden çok bloğa yazmaktan kaçınmak, ancak yine de bunları mroe kontrolü için bölebilmek için ilk blok boyunca akmak istediğiniz durumlar vardır. Ayrıca bir sürü başka neden var.


0

Bu eski bir soru ama aslında bugün vakayı break cümlesi olmadan kullanmaya başladım. Break kullanmamak, farklı işlevleri sırayla birleştirmeniz gerektiğinde aslında çok kullanışlıdır.

örneğin kullanıcının kimliğini zaman belirteci ile doğrulamak için http yanıt kodlarını kullanma

sunucu yanıt kodu 401 - belirteç güncel değil -> belirteci yeniden oluşturun ve kullanıcının oturum açın.
sunucu yanıt kodu 200 - belirteç tamam -> kullanıcı oturum açın.

durumda ifadeler:

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

Bunu kullanarak, 401 yanıtı için oturum açma kullanıcı işlevini çağırmanıza gerek yoktur, çünkü belirteç yeniden oluşturulduğunda, çalışma zamanı durum 200'e atlar.


0

Diğer türdeki sayıları, ayı, sayımı kolayca ayırabilirsiniz.
Bu, bu durumda daha iyidir;

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

Şu anda breakswitch ifademde ihtiyacım olan proje üzerinde çalışıyorum, aksi takdirde kod çalışmaz. Benimle birlikte kalın ve breaksize anahtar cümlenizde neden ihtiyacınız olduğuna dair iyi bir örnek vereceğim .

Biri kullanıcının bir sayı girmesini bekleyen, ikincisi onu hesaplamak için ve üçüncüsü toplamı yazdırmak için bekleyen üç durumunuz olduğunu hayal edin.

Bu durumda sahip olursunuz:

  1. Durum1 - Kullanıcının bir numara girmesini bekleyin
  2. State2 - Toplamı yazdır
  3. state3 - Toplamı hesaplayın

Devletler baktığınızda, talepleri sonucu sırası başlayacak isteyeyim state1 sonra, state3 ve nihayet state2 . Aksi takdirde, toplamı hesaplamadan yalnızca kullanıcıların girdilerini yazdıracağız. Tekrar açıklığa kavuşturmak için kullanıcının bir değer girmesini bekleriz, ardından toplamı hesaplar ve toplamı yazdırırız.

İşte örnek bir kod:

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

Kullandığımız yoksa break, bu düzen içinde çalıştırır state1 , state2 ve state3 . Ancak kullanarak break, bu senaryodan kaçınıyoruz ve durum1, sonra durum3 ve son fakat en az değil durum2 ile başlamak için doğru prosedürde sipariş verebiliriz.


-1

Kesinlikle, çünkü bazı akıllı yerleştirme ile blokları kademeli olarak çalıştırabilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.