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は「指定回数の繰り返し」に専念すべきであり、余計な条件を加えると混乱してしまう可能性がある。