Javaについて徹底解説!

Javaのswitch文の使い方 if文との使い分けや仕様変更も解説!

Javaのswitch文は、条件分岐の構文の一つです。この記事では、まずはswitch文の構文と使い方の基本をお伝えします。

なお、実際にswitchを使う際は、同じ条件分岐の構文であるif文との使い分けに迷うかもしれません。それに、長らく仕様が変わらなかったswitch文に、最近新しい仕様が追加される気配があります。

ですので、この記事ではswitch/if文の使い分けの考え方と、新しいswitch文の姿もお伝えします!!

1.switch文の基本

1-1.switch文の構文

switch文の基本的な構文は以下のものです。“switch” の後にある () に判定したい値・変数を指定し、“case” の後ろにマッチさせたい値・条件を指定し、必要なだけ羅列します。caseの後ろには“:”が必要ですので、忘れないようにしましょう。

caseは分岐に必要な分だけ記述できます。breakは必須ではありませんが、caseごとに記述することが普通でしょう(breakの使い方は後述します)

実際に良く見られるswitch文は以下のような感じです。式の評価結果はその都度変化し、値は固定となっていて、別の場所で宣言されている変数を参照するのが普通です。

if文で表現するなら、ifelse ifelseと似た意味になります。switch文は、長いelse-ifの条件を“==”いらずでシンプルに書けるのが良いところですね。また、switch文全体で式の評価が1回で済むのが、if文と比べてパフォーマンス面でも優れるところです。

1-2.switch文で使える型

1つのswitch文で扱える型は、以下のいずれか1つだけです(Java 10時点)。複数の型を混ぜては使えません。longや浮動小数点数(floatdouble)が使えないことにも注意しましょう。実務上ではint/String/enumが多いと思います。

  • char、byteshortintenum(J2SE 5以降)String(Java 7以降)
  • およびswitch文に対応するプリミティブ型のラッパークラス(CharByteShortInteger)

例として、式と値にintStringが混在している下記switch文は、コンパイルエラーとなってしまいます。

さらに、式・値の両方でnullは扱えません。値がnullだとコンパイルエラーになります。式がnullの場合は、コンパイルエラーでなく実行時エラー(NullPointerException)となりますので、実行するまでエラーとなることが分からず発見しづらいです。

switch文の値で使えるのは、それぞれの型のリテラル値・変数・演算結果です。しかも値はコンパイル時に確定していなければなりませんので、普通は静的に宣言した定数、あるいは定数に対する静的な演算結果を用います。enumのフィールドは定数扱いなので、そのまま指定できます。

例えば以下のような書き方ができます。プログラマーが読んだ時にわかりやすくするのにも使えたりしますね。

式と値が等しいかの判定は、Stringならequals()の結果で、それら以外(プリミティブ型のラッパークラス含む)なら==の結果です。

1-3.caseを中断するbreakとフォールスルー

breakはfor文やwhile文での意味と近く、実行しているcase句の処理をそこで打ち切り、switch文を抜けます。

case句の中にbreakがないと、その次にあるcaseに処理の流れがそのまま移ります。これをフォールスルー(fall through)と言います。日本語に直訳すれば「通り抜けて落ちる」で、そのまま下へすとーんと落ちていくイメージです。これがswitch文の大きな特徴の一つです。

この例だと、式が値1に該当する場合は文1が実行された後、値2の文2がそのまま実行されます。値2breakしていますので、そこでswitch文全体が終了します。これは、特定の値の時だけ先に追加で実行したい処理がある場合に使えます。

なお、case内には処理を書かなくてもいいので、複数の値で同じ処理を実行するだけなら、以下の書き方もできます。これはif文での「または(||)」と似ていますね。

1-4.defaultはその他の場合に使う

defaultは、全てのcaseに該当しない場合の処理を書く時に使用します。ifelse ifelseで言う、最後のelseに相当するものです。

なお、defaultswitch文のどこに出て来ても問題ありません。そのため、必ず最後に来るif文のelseとは扱いが違います。ですが、defaultelseと同様に、caseの一番最後に置くことが普通でしょう。

例えば、以下のswitch文も構文上は正しいものです。ここでdefaultでのbreakがないと後ろのcaseがフォールスルーで実行されてしまいます。特別な理由がなければ最後にしておくのが無難でしょう。

2.switch文のはまりポイントと対応方法

ここではswitch文でよくはまるポイントと対応方法をお伝えします。もしswitch文でトラブルが起きたら、以下のことに該当しないか調べてみてください。

2-1.式や値にnullは使えない

前述のとおり、switch文の式や値にnullは使えません。実行時にswitch文に関係する箇所でNullPointerExceptionが発生したなら、式に関係する変数が指しているものがnullでないかを確認しましょう。

2-2.桁の大きな整数や浮動小数点数は使えない

これも前述のとおり、式や値にlongfloatdouble(およびそれらのラッパークラスであるLongFloatDouble)は使えません。数値を表現するクラスであるNumberBigIntegerBigDecimalも同様に使えません。

2-3.通常のクラスやインターフェイスは使えない

switchで使える以外の型、つまり自分で作ったクラスやインターフェイスでもswitchはできません。

2-4.caseには同じ値を指定できない

caseには同じ値を指定できません。コンパイルエラーのメッセージもまだわかりやすいのですぐ対応できると思いますが、定数を羅列した場合に同じ値があるかどうかは分かりづらいので、定数の定義元をしっかり確認するようにしましょう。

2-5.switch文内の変数のスコープはブロック全体

これは結構ややこしいです。例えば、以下のswitch文はコンパイルエラーになります。ですので、なるべく複数のcaseにまたがるような変数は使わず、なるべく一つのcaseの中に収まるように使うのが良いでしょう。

2-6.switch内で宣言した変数はブロックの外では使えない

for/while/if文などと同じで、switch文全体が大きな一つのブロックになるので、{}の中で宣言した変数はswitch文の外部では使えません。見落としがちなので注意しましょう。

2-7.ネストしたswitchからbreakするにはラベル付きbreakを使う

あまりないと思いますが、switch文をネストさせたいことがあるかもしれません。その場合、ネストが深い方のswitch文をbreakしただけでは、そのswitch文だけがbreakされるので、大元のswitch文の後続処理はそのまま実行されます。

もし、ネストが深いswitch文から直接switch文全体をbreakしたいなら、ラベル付きbreakが使えます。例は以下のとおりです。

2-8.switchで処理したい値以外が来た時の対処方法

switchで処理したい値以外が来た時の対処方法にはいくつかあります。

1つ目はそもそも必要な値以外をcaseに書かないか、既にお伝えしたdefaultを使えば良いでしょう。defaultを使う場合も指定はするけれども、中では何もしないのです。何事もなかったかのごとく後続の処理が動けばいいならこれが最も簡単です。

2つ目は、defaultは使うけれども例外をthrowするものです。イディオムの一つでもあります。良く見かけるのはIllegalArgumentExceptionIllegalStateExceptionthrowするものですが、他の例外や自作の例外をthrowしてもOKです。if-elseelseで例外をthrowするのと同じですね。

これの良いところは、想定した値以外では絶対に後続処理が動かないことです。こういう書き方だと、例でのresultには必ず値が設定されることが保証されますので、変数の初期化漏れやデフォルト値のままだった…というようなうっかりミスを防げます。

それに、例外をthrowすることで「想定している値以外は絶対に許可しない」というプログラマーの意思を表現できます。1つ目のやり方だと、考慮漏れなのかあるいは分かっていてそうしているのか、設計書やコメントでも残っていなければわからないですよね。

3.switch文とif文との使い分けの判断基準

switch文とif文を使い分ける時の判断基準の一つは、判定処理にかかるコストと速さです。

if文は全ての条件判定を上から順番に行うので、柔軟な条件判定ができる代わりに、判定にかかるコスト(処理時間含む)が大きくなりがちです。判定条件が複雑になればなるほど、if文全体の実行コストが上がっていきます。良く発生する条件ほどif文の前の方に持ってくるべきなのは、これが理由です。

switch文は、該当する値の箇所へ処理が直接ジャンプする、いわゆる「GOTO文」のイメージです。GOTO文のような処理は選択肢がいくら多くてもジャンプに必要な時間はほぼ一定で、それがswitch文の特徴でもあります(ちなみに、GOTO文は最近のプログラミング言語には仕様上で存在しないことが多いです)

ですので、上のif文で例えるなら、switch文は途中の判定処理13を実行すらせず、いきなり判定条件4のブロック内に処理が移る、ということです。これがswitch文とGOTO文が同じと言えるところです。ただし、その代償としてswitch文では複雑な条件判定は行えません。

従って、switch文とif文の使い分けの方針の一つとしては、以下を考えて、当てはまるものが多ければswitch文…としても良いのではないのでしょうか。もちろん、見た目が好きというのもありなのですが、選んだ理由をきちんと言えると格好いいですよね。

  • 判定条件で使う変数は1つだけか
  • 判定条件で使う変数の型は1つだけ、かつswitch文で使えるものか
  • 判定条件は == あるいは equals だけか
  • 判定条件の値、総数がプログラミング時に静的に決定できるか
  • 判定処理の速度がプログラムの要件・仕様上で重要か

4.switchの未来、JEP 325:Switch Expressions

switchの構文そのものはJavaの初期から変わっていません。これまでのJava仕様の改定では、扱える値の型が増えただけでした。もちろん、switch文でenumStringが扱えるようになっただけでも大きな進歩です。

ですが、「JEP 325 Switch Expressions」によりswitch文の構文に大きな変化が訪れる予定なので、先取りして知識を得ておいても良いでしょう。JEP 325Java 12で取り込まれる可能性がある仕様です(2018/10時点)

JEP 325では、switch文自体が戻り値を返せるようになったり、ラムダ式のように使えるようになります。例えば以下のものはJEP 325から抜粋したものですが、もっとswitch文を活用できるようになりそうですね。

5.まとめ

この記事ではswitchの基本とはまりポイント、なぜswitchが早いのかをお伝えしました。switch文にはif文にはない特徴がありますので、ぜひ活用していきましょう。

また、現在時点で開発中のJEP 325では、switch文の構文が拡張されます。このような情報を先取りすることで、周囲の一歩先を行けるかもしれませんね。