Javaのgotoステートメントの代替


83

Javaのgotoキーワードの代替関数は何ですか?

Javaにはgotoがないので。


10
@harigm:Javaで利用できる場合に 作成するコードの種類の例を挙げてgotoいただけますか?そこにはもっと重要な問題があると思います。
多遺伝子潤滑剤

3
@harigm:2番目のポリジーンです。質問を更新して、プログラムにgotoが必要な理由を含めてください。
全員

13
人々はいつもgotoを使わないことについて話しますが、かなりよく知られていて使われている本当に良い実世界のユースケースがあると思います。つまり、関数から戻る前に必ずコードを実行してください。通常はリリースされます。ロックかどうかはわかりませんが、私の場合は、必要なクリーンアップを実行できるように、戻る直前に休憩にジャンプできるようにしたいと思います。もちろん、gotoがいたるところに散らばっているプログラムは恐ろしいものですが、メソッド本体に制限されている場合は、規則に従っている限り、それほど悪くはないようです(関数の最後にジャンプするだけで、バックアップしないでください)
Mattウルフ

7
あなたは良い仲間です。Linus Torvaldsは、gotokerneltrap.org / node / 553を熱心に擁護してきました。間違いなく、ProjectCoinからの最も注目すべき省略。
2011

7
@MattWolfeは試行しません-最後にその例の仕事をしますか?
Andres Riofrio 2012

回答:


83

ラベル付きのBREAKステートメントを使用できます。

search:
    for (i = 0; i < arrayOfInts.length; i++) {
        for (j = 0; j < arrayOfInts[i].length; j++) {
            if (arrayOfInts[i][j] == searchfor) {
                foundIt = true;
                break search;
            }
        }
    }

ただし、適切に設計されたコードでは、GOTO機能は必要ありません。


88
これは猛禽類からあなたを救うことはありません!
ボビー

25
適切に設計されたコードでgoto機能が必要ないのはなぜですか?
rogermushroom 2011年

13
それを使用できるのはアセンブリだけだからです。whileステートメント、forステートメント、do-while、foreach、関数呼び出し、これらはすべて、制御された予測可能な方法で使用されるGOTOステートメントです。アセンブラのレベルでは、常にGOTOになりますが、純粋なGOTOが提供する機能は必要ありません。関数やループ/制御ステートメントに対する唯一の利点は、コードの途中でどこにでもジャンプできることです。 。そして、その「利点」はそれ自体が「スパゲッティコード」の定義です。

1
@whatsthebeefこれは、エドガーW.ダイクストラによる1968年の有名な手紙で、彼が後藤を有害だと考えた理由を説明しています。
thomanski 2012


42

gotoJavaの概念に直接相当するものはありません。クラシックでできることのいくつかを実行できるようにするいくつかの構造がありますgoto

  • breakそしてcontinue文はループやスイッチ文でブロックからジャンプすることができます。
  • ラベル付きステートメントと break <label>任意の複合ステートメントから、特定のメソッド(または初期化ブロック)内の任意のレベルにジャンプできます。
  • ループステートメントにラベルを付けるcontinue <label>と、内側のループから外側のループの次の反復を続行できます。
  • 例外をスローしてキャッチすると、メソッド呼び出しの多くのレベルから(効果的に)ジャンプできます。(ただし、例外は比較的高価であり、「通常の」制御フロー1を実行するための悪い方法と見なされます。)
  • そしてもちろん、ありますreturn

これらのJava構造はいずれも、現在のステートメントと同じレベルのネストで、コード内のあるポイントに逆方向に分岐することはできません。それらはすべて、1つ以上のネスト(スコープ)レベルからcontinue飛び出し、(を除いて)すべて下にジャンプします。この制限は、古いBASIC、FORTRAN、およびCOBOLコード2に固有のgoto「スパゲッティコード」シンドロームを回避するのに役立ちます。


1-例外の最もコストのかかる部分は、例外オブジェクトとそのスタックトレースの実際の作成です。「通常の」フロー制御に本当に例外処理を使用する必要がある場合は、例外オブジェクトを事前に割り当て/再利用するか、fillInStackTrace()メソッドをオーバーライドするカスタム例外クラスを作成できます。欠点は、例外のprintStackTrace()メソッドが有用な情報を提供しないことです...それらを呼び出す必要がある場合。

2-スパゲッティコードシンドロームは、利用可能な言語構造の使用を制限する構造化プログラミングアプローチを生み出しました。これは、BASICFortran、およびCOBOLに適用できますが、注意と規律が必要でした。goto完全に取り除くことは、実用的に優れた解決策でした。あなたがそれを言語で保つならば、それを乱用する何人かの道化師が常にいます。


while(...){}for(...){}のようなループ構造を使用すると、任意の場所に明示的にジャンプすることなく、コードのブロックを繰り返すことができます。また、メソッド呼び出しmyMethod()を使用すると、他の場所で見つかったコードを実行し、終了時に現在のブロックに戻ることができます。これらはすべて、gotoが許可する戻りに失敗するという一般的な問題を防ぎながら、gotoの機能を置き換えるために使用できます。
クリスナバ2010年

@ Chris-それは本当ですが、GOTOをサポートするほとんどの言語は、Javaループ構造に近い類似物も持っています。
スティーブンC

1
@ Chris-古典的なFORTRANには「for」ループがあり、古典的なCOBOLにもループ構造があります(詳細は思い出せませんが)。唯一の古典的なBASICは...明示的なループ構造を持っていない
スティーブンC

31

楽しみのためだけに、ここではJavaでGOTOの実装があります。

例:

   1 public class GotoDemo {
   2     public static void main(String[] args) {
   3         int i = 3;
   4         System.out.println(i);
   5         i = i - 1;
   6         if (i >= 0) {
   7             GotoFactory.getSharedInstance().getGoto().go(4);
   8         }
   9         
  10         try {
  11             System.out.print("Hell");
  12             if (Math.random() > 0) throw new Exception();            
  13             System.out.println("World!");
  14         } catch (Exception e) {
  15             System.out.print("o ");
  16             GotoFactory.getSharedInstance().getGoto().go(13);
  17         }
  18     }
  19 }

それを実行する:

$ java -cp bin:asm-3.1.jar GotoClassLoader GotoDemo           
   3
   2
   1
   0
   Hello World!

「使わないで!」を追加する必要がありますか?


1
私はそれを使用し
ません

1
バグ:Math.random()0を返すことができます。> =
poitroae 2012

はい、そうです。バイトコードハッカリーによってのみ実装できるものは、実際にはJavaではありません...
Stephen C

行番号の代わりにラベルをサポートする同様のものはありますか?
behrouz.M 2016年

1
実装へのリンクが壊れています。
LarsH 2018

17

一部のコメント提供者や反対派は、これはgotoではないと主張していますが、以下のJavaステートメントから生成されたバイトコードは、これらのステートメントが実際にgotoセマンティクスを表現していることを実際に示唆しています。

具体的にはdo {...} while(true);、2番目の例のループは、ループ条件を評価しないようにJavaコンパイラーによって最適化されています。

前にジャンプ

label: {
  // do stuff
  if (check) break label;
  // do more stuff
}

バイトコード:

2  iload_1 [check]
3  ifeq 6          // Jumping forward
6  ..

後ろにジャンプする

label: do {
  // do stuff
  if (check) continue label;
  // do more stuff
  break label;
} while(true);

バイトコード:

 2  iload_1 [check]
 3  ifeq 9
 6  goto 2          // Jumping backward
 9  ..

do / whileはどのように後方にジャンプしますか?それはまだブロックから抜け出します。反例:ラベル:do {System.out.println( "Backward"); ラベルを続ける;} while(false); あなたの例では、while(true)が後方ジャンプの原因であると思います。
ベンホランド

@BenHolland:continue label後方へのジャンプです
Lukas Eder

私はまだ確信していません。Continueステートメントは、for、while、またはdo-whileループの現在の反復をスキップします。継続はループの本体の終わりまでスキップし、ループを制御するブール式について評価されます。しばらくは、継続ではなく後方にジャンプしているものです。docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html
Ben Holland

@BenHolland:技術的にはその通りです。論理的な観点から、// do stuff// do more stuffが関心のあるステートメントである場合、(もちろんステートメントのために)後方にジャンプするcontinue label 「効果があります」while(true)。しかし、これがそれほど正確な質問であることに気づいていませんでした...
Lukas Eder 2013年

2
@BenHolland:答えを更新しました。while(true)コンパイラによって翻訳されたgotoバイトコード操作。以下のようtrueに一定のリテラルで、コンパイラは、この最適化を行うことができますし、何を評価する必要はありません。だから、私の例は本当に後藤で、後ろにジャンプしています...
Lukas Eder 2013年

5

gotoステートメントのようなものが本当に必要な場合は、いつでも名前付きブロックに分割してみることができます。

ラベルを破るには、ブロックの範囲内にいる必要があります。

namedBlock: {
  if (j==2) {
    // this will take you to the label above
    break namedBlock;
  }
}

gotoを避けるべき理由については説明しません-その答えはすでに知っていると思います。


2
ここにリンクされているSOの質問にこれを実装してみました。実際には「上のラベル」に移動しません。おそらく著者の発言を誤解したかもしれませんが、わかりやすくするために、下にある外側のブロック(「ラベル付き」ブロック)の最後に移動することを付け加えておきます。あなたが壊しているところ。したがって、上向きに「壊す」ことはできません。少なくともそれは私の理解です。
名前空間

4
public class TestLabel {

    enum Label{LABEL1, LABEL2, LABEL3, LABEL4}

    /**
     * @param args
     */
    public static void main(String[] args) {

        Label label = Label.LABEL1;

        while(true) {
            switch(label){
                case LABEL1:
                    print(label);

                case LABEL2:
                    print(label);
                    label = Label.LABEL4;
                    continue;

                case LABEL3:
                    print(label);
                    label = Label.LABEL1;
                    break;

                case LABEL4:
                    print(label);
                    label = Label.LABEL3;
                    continue;
            }
            break;
        }
    }

    public final static void print(Label label){
        System.out.println(label);
    }

これにより、StackOverFlowExceptionが発生します。これを十分に長く実行すると、移動する必要があります。
ケビンパーカー

3
@Kevin:これはどのようにしてスタックオーバーフローにつながるのでしょうか?このアルゴリズムには再帰は...ありません
ルーカス・エデル

1
これは、「goto」を使用せずにプログラムを記述できるという元の証明と非常によく似ています。つまり、gotoよりも10倍悪い「label」変数を使用してwhileループに変換します。
gnasher729 2014年

3

StephenCは次のように書いています。

古典的なgotoでできることのいくつかを実行できるようにする2つの構造があります。

もう1つ...

マットウルフは書いています:

人々はいつもgotoを使わないことについて話しますが、かなりよく知られていて使われている本当に良い実世界のユースケースがあると思います。つまり、関数から戻る前に必ずコードを実行してください。通常はリリースされます。ロックかどうかはわかりませんが、私の場合は、必要なクリーンアップを実行できるように、戻る直前に休憩にジャンプできるようにしたいと思います。

try {
    // do stuff
    return result;  // or break, etc.
}
finally {
    // clean up before actually returning, even though the order looks wrong.
}

http://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html

試行ブロックが終了すると、finallyブロックは常に実行されます。これにより、予期しない例外が発生した場合でも、finallyブロックが確実に実行されます。しかし、最後に、例外処理だけでなく、プログラマーがリターン、続行、または中断によって誤ってクリーンアップコードをバイパスすることを回避できるようにします。例外が予想されない場合でも、finallyブロックにクリーンアップコードを配置することは常に良い習慣です。

finalに関連するばかげたインタビューの質問は次のとおりです。try{}ブロックから戻ったが、finally {}にも戻りがある場合、どの値が返されますか?


2
私はそれがばかげたインタビューの質問であることに同意しません。経験豊富なJavaプログラマー、ブロックにを入れることがなぜ悪い考えreturnであるを理解するためだけに、何が起こるか知っている必要がありfinallyます。(そして、知識が厳密に必要でなくても、私はそれを見つける好奇心を持っていなかったプログラマーによって心配されるでしょう...)
スティーブンC

1

最も簡単な方法は次のとおりです。

int label = 0;
loop:while(true) {
    switch(state) {
        case 0:
            // Some code
            state = 5;
            break;

        case 2:
            // Some code
            state = 4;
            break;
        ...
        default:
            break loop;
    }
}

0

以下のコードを試してください。わたしにはできる。

for (int iTaksa = 1; iTaksa <=8; iTaksa++) { // 'Count 8 Loop is  8 Taksa

    strTaksaStringStar[iCountTaksa] = strTaksaStringCount[iTaksa];

    LabelEndTaksa_Exit : {
        if (iCountTaksa == 1) { //If count is 6 then next it's 2
            iCountTaksa = 2;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 2) { //If count is 2 then next it's 3
            iCountTaksa = 3;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 3) { //If count is 3 then next it's 4
            iCountTaksa = 4;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 4) { //If count is 4 then next it's 7
            iCountTaksa = 7;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 7) { //If count is 7 then next it's 5
            iCountTaksa = 5;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 5) { //If count is 5 then next it's 8
            iCountTaksa = 8;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 8) { //If count is 8 then next it's 6
            iCountTaksa = 6;
            break  LabelEndTaksa_Exit;
        }

        if (iCountTaksa == 6) { //If count is 6 then loop 1  as 1 2 3 4 7 5 8 6  --> 1
            iCountTaksa = 1;
            break  LabelEndTaksa_Exit;
        }
    }   //LabelEndTaksa_Exit : {

} // "for (int iTaksa = 1; iTaksa <=8; iTaksa++) {"

-1

gotoの代わりに、ラベル付きのブレークを使用します。


この答えは、常に冗長でした
Stephen C

-1

Javaにはgoto、コードが構造化されおらず、読みにくくなるため、がありません。ただし、使用することができますbreakし、continueなど文明の形後藤その問題なし。


ブレークを使用して前にジャンプ-

ahead: {
    System.out.println("Before break");
    break ahead;
    System.out.println("After Break"); // This won't execute
}
// After a line break ahead, the code flow starts from here, after the ahead block
System.out.println("After ahead");

出力

Before Break
After ahead

続行を使用して後方にジャンプする

before: {
    System.out.println("Continue");
    continue before;
}

これにより、行が実行されるたびにコードフローがから再開されるため、無限ループが発生します。continue beforebefore


2
Continueを使用して後方にジャンプする例は間違っています。ループの本体の外で「続行」を使用することはできません。コンパイルエラーが発生します。ただし、ループ内では、continueは単にループを条件付きでジャンプします。したがって、while(true){continue;}のようなものは無限ループになりますが、それに関してはwhile(true){}も同様です。
ベンホランド
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.