gotoがいやならメソッドを使えばいいじゃない
同僚の人はフラグを使ったやりかた
2008-12-27 - プログラマとSEのあいだ
〜プログラムは略〜
や、例外を使ったやりかた
〜プログラムは略〜
で、いままでやってきたらしい。
なんで?って聞いたら、goto絶対ダメ派なんだそうな。
ラベル付きbreakはgotoじゃないと思うけど、なかなか納得してくれんかったなあ。
ラベル付きbreakは許せなくて、例外は許せるってのはちょっとよくわからん考え方だな。
そんなにラベルつきのbreakがいやなら、メソッド使ってラベルなしのbreakを使うというのはどうだろう。
public class DoubleLoop { public static void main(String[] args) { for (int i = 0; i < 10; i++) { if (!innerLoop(i + 1)) break; // 追記:この例ではreturn;の方がいい。コメント参照。 } } private static boolean innerLoop(int outer) { for (int i = 0; i < 10; i++) { int inner = i + 1; System.out.printf("i = %d, j = %d%n", outer, inner); if (outer % 3 == 0 && inner % 3 == 0) return false; } return true; } }
mainメソッド内のif文の中が気持ち悪いなら、ちょっと説明的にしてみるとか。
public class DoubleLoop { private static enum InnerLoopResult { BREAK(true), CONTINUE(false); final boolean isBreak; private InnerLoopResult(boolean isBreak) { this.isBreak = isBreak; } } public static void main(String[] args) { for (int i = 0; i < 10; i++) { if (innerLoop(i + 1).isBreak) break; // 追記:この例ではreturn;の方がいい。コメント参照。 } } private static InnerLoopResult innerLoop(int outer) { for (int i = 0; i < 10; i++) { int inner = i + 1; System.out.printf("i = %d, j = %d%n", outer, inner); if (outer % 3 == 0 && inner % 3 == 0) return InnerLoopResult.BREAK; } return InnerLoopResult.CONTINUE; } }
個人的には最初の例で十分だけどね。
それぞれの利点・欠点はこんな感じ?
利点 | 欠点 | |
---|---|---|
ラベルつきbreak | 読みやすい 3重ループ以上にネストしても可読性が落ちない |
処理が複雑になると可読性が落ちる |
フラグ | 細かい制御ができる | ループがネストすると可読性は皆無 2重ループでさえやりたいことを素直に記述できない |
例外 | 読みやすい 3重ループ以上にネストしても可読性が落ちない |
インデントが深くなる パフォーマンスが悪い catch (Exception e)と書いてしまうと、本当の例外を握りつぶしてしまう |
メソッドに分ける (booleanで判定) |
ループ内の処理が複雑になっても可読性が落ちない | 3重ループ以上にネストすると、他の方法と併用するか、さらに複雑な記述が必要 breakの判定が少し分かりにくい |
メソッドに分ける (enumで判定) |
ループ内の処理が複雑になっても可読性が落ちない 外側のループに相当する部分の可読性が高い |
3重ループ以上にネストすると、他の方法と併用するか、さらに複雑な記述が必要 やってることに対してコードの量が多い |
例外を使った場合のパフォーマンス低下だけど、2重ループの外側に大きなループがなければ、十分無視できるかもしれない。
かといって、例外を通常のフローの制御に使うのは反対だけどね。
もしやるとしたら、最大限例外じゃないことを明示するとか?
public class DoubleLoop { // privateなクラスにして、さらにExceptionというサフィックスを付けない private static class LoopBreakSignal extends Exception { ... } public static void main(String[] args) { try { ... } catch (LoopBreakSignal s) { // eじゃなくてs // フロー制御に例外を使用しています。 // LoopBreakSignalをExceptionやThrowableに変更しないでください } } }
うーん、意味的にはThrowableから派生させたほうがいいのかな。