web-dev-qa-db-ja.com

なぜcaseステートメントの後にブレークが必要なのですか?

コンパイラがスイッチの各コードブロックの後にbreakステートメントを自動的に配置しないのはなぜですか?歴史的な理由ですか?複数のコードブロックをいつ実行しますか?

91
unj2

次のように、同じコードブロックに複数のケースを関連付けると役立つ場合があります。

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

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

など。ほんの一例です。

私の経験では、通常、「フォールスルー」して1つのケースで複数のコードブロックを実行するのは悪いスタイルですが、状況によっては使用することもあります。

91
WildCrustacean

歴史的に 、これはcaselabelの-​​ ターゲットポイント としても知られているgotoを本質的に定義していたためです。コール。 switchステートメントとそれに関連するケースは、実際には、コードストリームへの複数の潜在的なエントリポイントを持つ多方向ブランチを表しています。

とは言っても、breakがほぼ常にデフォルトの動作であり、すべてのケースの終わりにそうすることがほぼ無限の回数記録されています。

30
Bob Cross

JavaはCに由来し、それがCの構文です。

複数のcaseステートメントに実行パスを1つだけ持たせたい場合があります。以下は、1か月の日数を示すサンプルです。

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);
    }
}
28
Romain Hippeau

ケースフォールスルーを使用して、あらゆる種類の興味深いことを実行できます。

たとえば、すべてのケースで特定のアクションを実行したいが、特定のケースではそのアクションに加えて他のアクションを実行したいとします。フォールスルーで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ステートメントを省略すると警告を表示します。

13
Zach Johnson

それは間違いだと思います。言語の構成要素として、breakをデフォルトとして使用し、代わりにfallthroughキーワードを使用するのは簡単です。私が書いて読んだコードのほとんどは、すべてのケースの後に中断があります。

13
James K Polk

コンパイラがスイッチの各コードブロックの後にbreakステートメントを自動的に配置しないのはなぜですか?

goodいくつかのケースで同じブロックを使用できるようにしたい(特別なケースである可能性がある).

歴史的な理由ですか?複数のコードブロックをいつ実行しますか?

これは主にCとの互換性のためであり、おそらくgotoキーワードが地球をうろついた昔からの古代のハックです。それはdoesもちろん Duff's Device のような驚くべきことを可能にしますが、それが賛成か反対かという点です...せいぜい議論的です。

7
Donal Fellows

したがって、同じことを行うためにいくつかのケースが必要な場合、コードを繰り返す必要はありません。

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

または、次のようなことができます:

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

カスケード方式で。

あなたが私に尋ねると、本当にバグ/混乱しやすい。

4
Francisco Soto

JavaはCから派生しており、その遺産には Duff's Device として知られる技術が含まれています。これは、break;ステートメントがない場合に、あるケースから次のケースに制御が移行するという事実に依存する最適化です。 Cが標準化された頃には、そのような「野生の」コードがたくさんあり、そのような構造を破るために言語を変更するのは逆効果だったでしょう。

3
Jim Lewis

歴史的な記録に関する限り、トニー・ホアは「構造化プログラミング」革命の間に、1960年代にケースステートメントを発明しました。 Tonyのcaseステートメントは、ケースごとに複数のラベルをサポートし、breakステートメントを使用せずに自動終了しました。明示的なbreakの要件は、BCPL/B/Cラインから出たものです。デニスリッチーは次のように書いています(ACM HOPL-II):

たとえば、BCPL switchonステートメントからエスケープするエンドケースは、1960年代に学習したときに言語に存在していなかったため、BおよびC switchステートメントからエスケープするbreakキーワードのオーバーロードは、意識的ではなく分岐した進化によるものです。変化する。

私はBCPLに関する歴史的な著作を見つけることができませんでしたが、リッチーのコメントは、breakが多かれ少なかれ歴史上の事故であったことを示唆しています。 BCPLは後でこの問題を修正しましたが、おそらくリッチーとトンプソンはUnixの発明に忙しすぎて、そのような詳細に煩わされることはありませんでした:-)

3
Norman Ramsey

Switch breaksの後のcaseは、switchステートメントのフォールスルーを回避するために使用されます。興味深いことに、これは現在、 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
3
Naman

前に言ったように、それはフォールスルーを許可することであり、間違いではなく、機能です。 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

予想通り。

1
Víctor Gil

コンパイラによって自動ブレークが追加されていないため、スイッチ/ケースを使用して、1と2からbreakステートメントを削除することで、1 <= a <= 3などの条件をテストできます。

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;
}
0
Soufiane Hassou

たとえば、複数のブロックに同じコードを書くことを避けながら、mroe制御のためにそれらを分割できるようにするために、最初のブロックを通過したい場合があるためです。他にもたくさんの理由があります。

0
Jonas B

他の種類の数字、月、カウントを簡単に分離できます。
これは、この場合よりも優れています。

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
John Dev

私は今、switchステートメントでbreakを必要とするプロジェクトに取り組んでいます。そうしないとコードが機能しません。 switch文でbreakが必要な理由の良い例をお見せします。

3つの状態があるとします。1つはユーザーが数値を入力するのを待ち、2つ目は数値を計算し、3つ目は合計を印刷します。

その場合、次のものがあります。

  1. State1-ユーザーが数値を入力するのを待つ
  2. State2-合計を出力する
  3. state-合計を計算する

状態を見ると、厳密な順序はstate1で始まり、次にstateで始まり、最後にstate2になります。それ以外の場合、合計を計算せずにユーザー入力のみを印刷します。もう一度明確にするために、ユーザーが値を入力するのを待ってから、合計を計算して合計を出力します。

コードの例を次に示します。

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を使用しない場合、state1state2、およびstateの順序で実行されます。しかし、breakを使用すると、このシナリオを回避し、state1で始まり、state3で始まり、最後になりますがstate2で​​はない正しい手順で順序付けできます。

0
Dler

これは古い質問ですが、実際には今日、breakステートメントのないcaseを使用することになりました。ブレークを使用しないことは、異なる機能を順番に組み合わせる必要がある場合に実際に非常に役立ちます。

例えばHTTP応答コードを使用して、タイムトークンでユーザーを認証する

サーバー応答コード401-トークンが古くなっています->トークンを再生成してユーザーをログインします。
サーバー応答コード200-トークンはOK->ログインユーザー。

caseステートメントの場合:

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