switch 文 (switch statement)
switch文(switch statement)は、多くの選択肢から1つを選んで実行する。
たとえば、
if ( n == 2 ) B1 else if ( n == 5 ) B2 else B3
は、nの値が2ならB1を、5ならB2を、それ以外ならB3を、それぞれ実行するif文であるが、これをswitch文で書けば次のようになる。
switch (n) { case 2: B1 break; case 5: B2 break; default: B3 }
このswitch文では、まずnの値が求められ、その値が2であれば、case 2:のあとに書かれた文の列が実行され、その値が5であれば、case 5:のあとに書かれた文の列が実行され、そのどれでもない場合はdefault:のあとに書かれた文の列が実行される。
break;
はbreak文(break statement)と呼ばれ、これを実行するとこのswitch文の実行が終わる。もし、B1の後のbreak文がないと、nの値が2のときにはB1を実行した後でB2が実行されてしまう。B3のあとにbreak文が書いてないのは、B3がswitch文の最後にあるから、break文がなくてもB3を実行したらこのswitch文の実行が終わるからである。
B3の後に明示的にbreak文を書いても良い。
switch (n) { case 2: B1 break; case 5: B2 break; default: B3 break; }
上記のswitch文からbreak文を取り除くと以下のような形になる。
switch (n) { case 2: B1 case 5: B2 default: B3 }
これは、次のようなif-elseの形で書き直すことができる。
if ( n == 2 ) { B1 B2 B3 } else if ( n == 5 ) { B2 B3 } else { B3 }
一般に、switch文は次の形である。
switch (整数型の式) { case 整数型の定数: 文の列 case 整数型の定数: 文の列 ... default: 文の列 }
このswitch文は次のように実行される。まず「整数型の式」の値が計算され、その値と等しい「整数型の定数」の書かれているcaseが選ばれて(この定数はcaseのラベルとよばれる)、その後に書かれている「文の列」が実行される。もし、「文の列」の最後にbreak文がないと、続けてその次のcaseの「文の列」を実行する。「整数型の式」の値と等しい「整数型の定数」がなければ、「default:」のあとの「文の列」が実行される。この最後の「default:」とそのあとの「文の列」は書かなくてもよい。しかし、それが書いてない場合で、「整数型の式」の値がどのcaseの値とも一致しない場合は、どの「文の列」も実行せずにswitch文の実行が終了してしまう。そのようなことが起きないように、いつでもdefault:を書いておくのが安全である。
case~の末尾は「:(コロン)」であることに注意すること。
caseラベルの定数式
caseラベルには整数型の定数を書くといったが、より正確には定数式を書くことが出来る。定数式は定数だけを使った式で変数などを含まないものである。たとえば
3 + 5 + 9
は定数式であり、次の2つのcaseラベルは同じになってしまう。
case 3 + 5 + 9: case 17:
同じラベルを持つcaseが一つのswitch文に含まれていると、どちらを優先すればいいのか曖昧になってしまうため、コンパイルエラーとなる。
switch 文の例
switchを用いる例として、以下のようなプログラムが挙げられる。
package j1.lesson06; import java.io.*; public class Switch3 { public static void main(String[] args) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); System.out.print("整数を入力してください:"); int input = Integer.parseInt(reader.readLine()); // 入力された値を用いて switch switch (input) { // 入力された値が 0 case 0: System.out.println("0が入力されました"); break; // 入力された値が 1 case 1: System.out.println("入力された値は1"); break; // 入力された値が 2 case 2: System.out.println("2です"); break; // 入力された値がそれ以外 default: System.out.println("0でも1でも2でもありません"); } } }
上記のプログラムは、入力された値が0から2のときはそれぞれの場合に応じたメッセージを表示し、それ以外の場合は「0でも1でも2でもありません」と出力する。
caseの扱い
caseラベルは次のように使用することもできる。
package j1.lesson06; import java.io.*; public class MultiCase { public static void main(String[] args) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); System.out.print("整数を入力してください:"); int input = Integer.parseInt(reader.readLine()); // 10 で割った余りについて調べる switch (input % 10) { // 10 で割り切れる case 0: System.out.println("10の倍数です"); break; // 10 で割った余りが 1 または 9 case 1: case 9: System.out.println("もう少しで10の倍数でした"); break; // 10 で割った余りが 5 case 5: System.out.println("10の倍数ではありませんが5の倍数です"); break; // それ以外 default: System.out.println("10の倍数ではありません"); } } }
これは
case 1: case 9:
と、2つのcaseを並べることによって、switchで指定した値が 1 または 9 の時に同じ処理をさせることができる。例では2つを並べたが、それ以上のcaseを並べても良い。
しかし、余りに多くのcaseを並べると読みにくいプログラムになってしまう可能性がある。その場合は if-else の連鎖によって実現したほうが読みやすい場合が多い。
switch 以外での break
switch文で使用したbreakは、switch文以外でもいくつかの場面で使用できる。
for 文での break
for 文を用いた繰り返しの中で break 文を使用すると、繰り返しをその時点で終了させることができる。
for (int i = 0; i < 100; i++) { if (i == 1) { break; } System.out.println(i); }
上記のプログラムの断片を実行した場合、
0
とだけ表示して for 文を抜けることになる。
while 文での break
while 文の中で break 文を使用すると、for 文と同様に繰り返しをその時点で終了させることができる。
int hoge = 1; while (hoge <= 10000) { hoge *= 2; break; }
break 文の注意
次のプログラムはどのような動きをするだろうか。
package j1.lesson06; public class ForSwitch { public static void main(String[] args) { for (int i = 0; i <= 5; i++) { switch (i) { case 0: case 2: case 4: System.out.println("偶数"); // この break は switch を抜ける break break; case 1: case 3: case 5: System.out.println("奇数"); // この break は switch を抜ける break break; default: // ここには来ない } } } }
上記のプログラム内に出てくる break 文は、コメントにもあるように switch 文を抜けるための break である。
switch から その外側にある for による繰り返しを終了させる方法もあるが、ここでは紹介しない (興味があれば調べてみよ)。
ネストした繰り返しの中で break 文を使用すると、一番内側の繰り返しだけ終了させることになる。
package j1.lesson06; public class ForFor { public static void main(String[] args) { for (int i = 1; i <= 5; i++) { for (int j = 1; j <= 5; j++) { System.out.println(i + j); break; } } } }
上記の例を実行させた場合、表示される結果は以下のようになる。
2 3 4 5 6
繰り返し内での break を使う場面
繰り返しの中でbreak文を使用すると、繰り返しの出口が2つ以上できてしまうことになる。
for (int i = 1; i <= 10 ; i++) { if (i == 5) { break; // 繰り返しの出口 (1) } else { System.out.println(i); } } // 繰り返しの出口 (2)
上記のような簡単な繰り返しならば問題ないが、複雑な繰り返しの様々な場所から脱出すると、繰り返しの条件が見づらくなってしまう。
繰り返し構文にはプログラムのバグ (不具合) が混入しやすい。以下の2つの繰り返しは同じ動作をするが、どちらが読みやすいだろうか?
for (int i = 1; i <= 10 ; i++) { System.out.println(i); }
for (int i = 1; i <= 11 ; i++) { System.out.println(i); if (i >= 10) { break; } }
breakを使用すると、繰り返し条件の表明が点在してしまい複雑になってしまう。break文を使用するもっともな理由がない限りは使用すべきではない。
例えば、以下のような場面では良く使う。
次のプログラムは、「valueが素数であるか判定する」簡単なプログラムである。
int divisor = 0; for (int i = 2; i < value; i++) { // 割り切れるか調べる if (value % i == 0) { divisor = i; // 割り切れたので終了 break; } } // 割り切れる値がなかった if (divisor == 0) { System.out.println(value + " is prime"); } // 1 と 自分自身以外で割り切れた else { System.out.println(value + " is not prime"); }
上記の例のように、繰り返しの中で「何かを一つでも見つける」というようなプログラムを書く場合、見つけるべきものを検出したら break してよい。
これを次のように書くと分かりにくくなる場合がある。
int divisor = 0; for (int i = 2; i < value && divisor == 0; i++) { if (value % i == 0) { divisor = i; } } ...
forは「指定回数の繰り返し」に専念すべきであり、余計な条件を加えると混乱してしまう可能性がある。