ケースステートメントの後に改行が必要なのはなぜですか?


94

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


2
JDK-12について回答し、強制しないようにラベルを変更しましたbreak
ナマン

回答:


94

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

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

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

など。単なる例です。

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


28
// Intentional fallthrough.改行を省略する場合は、常にコメントを行に追加してください。私の意見では「誤って休憩を忘れるのは簡単」ほど悪いスタイルではありません。PSもちろん、答え自体のような単純なケースではありません。
doublep '25

@doublep-同意します。私の意見では、可能であればそれを避けたいと思いますが、それが理にかなっている場合は、あなたが何をしているかを非常に明確にしてください。
WildCrustacean 2010

6
@doublep:複数caseのがそのようにスタックされている場合、コメントは気にしません。それらの間にコードがある場合、はい、コメントはおそらくメリットがあります。
ビリーONeal

4
私はあなたが一つの中に複数のケースを宣言することができます言語を想像case:そうのように、case 'A','B','C': doSomething(); case 'D','E': doSomethingElse();例の間に切れ目を必要とせずに、。Pascalはそれを行うことができます。「caseステートメントは、定数、部分範囲、またはコンマで区切られたそれらのリストである可能性がある各セレクターと順序式の値を比較します。」(wiki.freepascal.org/Case
クリスチャンSemrau

32

歴史的にので、それはだcase、本質的に定義されたlabelとしても知られ、ターゲットポイントgoto呼び出しを。switchステートメントとそれに関連するケースは、実際には、コードのストリームへの複数の潜在的なエントリポイントを持つ多方向分岐を表します。

そうは言っても、breakほとんどの場合、ほとんどすべてのケースの最後に持っていたいデフォルトの動作であるほとんど無限の回数が記録されています。


30

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

複数のcase文に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);
    }
}

4
誰かが上向きの矢を狙って逃した?それとも、彼らは...あなたのブレーススタイルやインデントと牛肉を持っていた
ジム・ルイス

ダンノ、だから私から+1をもらう。これはフォールスルーが役立つ例ですが、Javaがより現代的なケースステートメントを選んでくれれば幸いです。CobolのEVALUATE-WHEN-OTHERWISEははるかに強力であり、Java よりも古くなっています。Scalaの一致表現は、何ができるかの現代的な例です。
ジムフェラン2010

1
私の生徒はこれを行うために公的にむち打たれるでしょう。それは醜いコヨーテです。
ncmathsadist 2014

2
@ncmathsadist何かを行う1つの方法のポイントを示しています。この例がおそらく極端であることに異論はない。しかし、これは実際の例であり、人々が概念を理解するのに役立つと思います。
Romain Hippeau 2014

15

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


4
どちらがcontinue <case name>、どのcaseステートメントで続行するかを明示的に指定できるようにすることをお勧めします。
Vilx- 2010

4
@Vilx case現在の中switchで任意のものを許可すると、これは単にになりますgoto。;-)
クリスチャンセムラウ2011

13

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

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


Javaの文字列でスイッチ/ケースを使用できますか?
Steve Kuo

@Steve:おっと、現時点ではそうは思いません。stackoverflow.com/questions/338206/…によると、Javaの将来のバージョンでは文字列が許可される予定です。(私は現在、ほとんどのプログラミングをC#で行っています。これにより、switchステートメントで文字列を使用できます。)誤解を招く引用符を削除するために、回答を編集しました。
ザックジョンソン

2
@ZachJohnson、それよりずっと後のことですが、Java 7では文字列のスイッチを許可しています。
Bob Cross

7

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

ともかく良い(特別同棲することができる)いくつかのケースのための同一のブロックを使用することができるようにする欲求を...

それは歴史的な理由によるものですか?いつ複数のコードブロックを実行しますか?

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


5

break後スイッチcaseSは、スイッチ文のフォールスルーを回避するために使用されます。興味深いことに、これは、JEP-325を介して実装された、新しく形成されたスイッチラベルによって実現できます。

これらの変更により、さらに説明されbreakているようにwith everyスイッチ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

4

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

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

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

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

カスケード方式で。

あなたが私に尋ねれば、本当にバグ/混乱が起こりやすい。


その実行の両方んdo thisし、do thatこのためではなく、ただdo thatそのために?
JonnyRaa 14

1
ただドキュメントを読んでください。それはひどい!バグを書くのはなんと簡単なことでしょう。
JonnyRaa 2014

4

歴史的な記録に関して言えば、トニー・ホアレは、「構造化プログラミング」革命の最中の1960年代に事件の声明を発明しました。Tonyのcaseステートメントは、ケースごとに複数のラベルをサポートし、悪臭のない自動終了をサポートしましたbreak。明示的な要件breakは、BCPL / B / Cラインから生まれたものです。Dennis Ritchieは次のように書いています(ACM HOPL-II):

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

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


これはより多くの票を得るべきです。どうやらOP breakは、「複数のコードブロックの実行」を許可することで、この設計を選択する動機にもっと関心があることをすでに知っているようです。他の人はCからJavaへのよく知られた遺産について言及しました、そしてこの回答は研究をCより前の日にさらに押し上げました。最初からこの(非常に原始的ではありますが)パターンマッチングがあればいいのですが。
wlnirvana

3

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


1

前にも言ったように、フォールスルーを許すことで間違いではないのが特徴です。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

予想通り。


0

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

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

イェッチ。私はこれが全く嫌いです。
ncmathsadist 2014

0

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


0

それは古い質問ですが、実際には、breakステートメントのないケースを今日使用しました。異なる関数を順番に組み合わせる必要がある場合、ブレークを使用しないことは実際には非常に便利です。

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

サーバー応答コード401-トークンが古くなっています->トークンを再生成してユーザーをログインさせてください。
サーバー応答コード200-トークンは問題ありません->ユーザーをログインさせてください。

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

他のタイプの数、月、カウントを簡単に分離できます。
この場合、これはより良い方法です。

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

私は現在break、switchステートメントで必要なプロジェクトに取り組んでいます。そうしないと、コードが機能しません。私と一緒に耐えてください、そして私はあなたがbreakあなたのswitch文で必要な理由の良い例をあなたに与えます。

3つの状態があり、1つはユーザーが数値を入力するのを待機し、2つ目は数値を計算し、3つ目は合計を出力することを想像してください。

その場合、あなたは持っています:

  1. 状態1-ユーザーが数字を入力するのを待つ
  2. 状態2-合計を印刷する
  3. state3-合計を計算します

状態を見ると、厳密な順序はstate1から始まり、次にstate3、最後に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;
    }
}

を使用しない場合breakstate1state2state3の順に実行されます。しかしbreak、を使用すると、このシナリオを回避し、正しい手順で順序1で開始し、次に状態3、最後に状態2の順に並べることができます。


-1

正確には、いくつかの巧妙な配置により、ブロックをカスケードで実行できるからです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.