概要
- Javaにはif-then構文と、if-then-else構文の2つがある
- それぞれの
true
/false
に応じた実行文は、1つの文やブロックなどでなければならない - else ifという構文はなく、else句の実行文にif-thenやif-then-else文を連ねたもの
- if-then文やif-then-elseには原則として
{}
ブロックを使うべき
Java仕様におけるif/if-else/else ifの扱い
Oracleの仕様書では、if文の種類としてif-then文とif-then-else文が定義されている。しかし、”else if”という定義はされていない。
if-then文
if-then文の書式は以下のように定義されている。
IfThenStatement: if (Expression) Statement
Oracleの仕様によれば、Statementとして書けるのは以下の6つとされている。
- StatementWithoutTrailingSubstatement
- LabeledStatement
- IfThenStatement
- IfThenElseStatement
- WhileStatement
- ForStatement
いずれも単一の文であり、複数の文を書くことはできない。
if-then-else文
一方、if-then-else文は以下のように定義されている。
IfThenElseStatement: if (Expression) StatementNoShortIf else Statement
そしてStatementNoShortIfとして書けるのは次のいずれかとされている。
- StatementWithoutTrailingSubstatement
- LabeledStatementNoShortIf
- IfThenElseStatementNoShortIf
- WhileStatementNoShortIf
- ForStatementNoShortIf
こちらも複数の文を書くことはできない。
ブロックは書ける
上記のように、if-then文やif-then-else文には、独立した複数の文を書くことはできない。
ただし双方で記述可能なIfThenStatementを見ると、Blockが記述可能となっている。
- Block
- EmptyStatement
- ExpressionStatement
- ・・・・・
したがって{}
ブロックを使えば、他言語のif…else…endif構文のように複数の文を並べることができる(逆にそうしなければ書けない)。
else if
ブロックで書く
ところで、Javaの仕様ではelseifに相当するキーワードも構文も定義されていない。ただし、else句の後にif-then文やif-then-else文を書いて、同様のロジックとすることはできる。ただし、このときに原則として{}
ブロックで書くべきである。
1 2 3 4 5 6 7 |
if (...) { ..... } else if (...) { ..... } else { ..... } |
ブロック化すべき理由
先の仕様で、if-then文のStatementにはifTthenStatementとifThenElseStatementがかけるが、if-then-else文のif-then句ではifThenElseStatementが書けないことになっている。これが何を意味するか試してみよう。
まずif-then文の実行文としてifThenStatementあるいはifThenElseStatementを連ねた場合。想定通り真偽条件に応じた結果が出力されている。
1 2 3 4 5 |
if (true) if (true) System.out.println("Reached!"); // Reached! if (true) if (false) System.out.println("Not heare."); else System.out.println("Reached!"); // Reached! |
次にif-then-elseの第1ステートメントにifThenElseStatementを書いた場合。この場合も想定通りの結果になる。
1 2 3 4 5 |
if (true) if (false) System.out.println("Not heare."); else System.out.println("Reached!"); else System.out.println("Came here!"); // Reached! if (false) if (false) System.out.println("Not heare."); else System.out.println("Reached!"); else System.out.println("Came here!"); // Came here! |
ここで、仕様で想定されていない構成にしてみる。if-then-else文の第1ステートメントには、ifThenElseStatementが想定されているが、ifThenStatementは想定されていない。これを無視してifThenStatementを書いたのが以下。
1 2 |
if (true) if (false) System.out.println("Not heare."); else System.out.println("Came here!"); // Came here! |
これは インデントで表現すると以下のようなロジックを想定している。ところがその想定と異なる動作になってしまう。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
if (true) if (false) System.out.println("Not heare."); else System.out.println("Came here!"); // 1つ目のifはtrue→直下の2つ目のifへ // →2つ目のifはfalse→2つ目のif文を抜ける // →elseは1つ目のifがtrueなので対象外 // →このブロックを抜ける // →何も表示されないはず・・・ // しかしCame here!と表示される |
このことはOracleの仕様書14.5. Statementsに書かれていて、”else
句は(そのelse
より前の)最も内側のif
に属する”こととされている。つまり上の文を解釈通りのインデントにすると以下のようになる。
1 2 3 4 5 6 7 8 9 |
if (true) if (false) System.out.println("Not heare."); else System.out.println("Came here!"); // 1つ目のifはtrue→直下の2つ目のifへ // →2つ目のifはfalse→else句へ // Came here! |
もしこの文を当初のロジックにしたければ、以下のようにブロック化しなければならない。
1 2 3 4 5 |
if (true) { if (false) System.out.println("Not heare."); }else { System.out.println("!!Came here!"); } |
これで想定通り何も表示されず、想定ロジック・インデント通りの動作となる。
このようなelse句の解釈や、if-then文のStatementを追加する場合を考慮すると、たとえ1行の文でもブロック化すべきということになる。
余談
StatementNoShortIfとしてforやwhileの単文も書くことができる。
1 2 3 4 5 6 7 |
if (true) for (int i = 0; i < 5; i++) System.out.println(i); // 0 // 1 // 2 // 3 // 4 |
しかしながら、こういう表現も思わぬトラブル防止のためにブロック内に書いておいた方が安全だろう。