web-dev-qa-db-ja.com

ループの「続行」と「中断」はJavaのアンチパターン/悪い習慣ですか?

私はプログラミングにおけるcontinueの主な目的を理解しています。それは、次のようなループの残りのステップから抜け出すことができます。

while(condition1){
  ... code ...
  if(!condition2){
    continue;
  }
  ... code ...
  if(condition3){
    break;
  }
  ... code ...
}

Condition2とcondition3は内部コードに依存している可能性があるので、ループステップとは別にエスケープルートを作成すると便利です。しかし、それは、元の条件を再考するように、エスケープルートを構築する代わりにコードをリファクタリングする必要があることを意味します1。

私がいくつかのcontinuebreakのパーツが必要な場合、私は自分のコードに不満を感じます。通常、私はcontinueなしで作業していて、本当に見逃していません。

私が受け入れることのできるcontinuebreakのもう1つの使用法は最適化ですが、私は上記で述べたように、それについて同じ気持ちをまだ持っています。どういうわけか「GOTOを使う」と感じています。 そうですか、そうでなければ、なぜですか?

1
CsBalazsHungary

修正:continueは、ループ全体ではなく、ループの1回の反復から抜け出します。

ループの反復の残りをスキップすることは、メソッド呼び出しから早期に戻ることによく似ています(実際、長いループ本体の多くすべきはメソッド呼び出しです)。したがって、「return初期またはネストされたif?を使用する」と同じ議論が当てはまります。これは、よく知られている、対立する論争です。この質問についても、コンセンサスが得られるとは思いません。

私の立場は次のとおりです。continueは理由のあるキーワードです。ほとんどの人がgotoを悪いと思っているのと同じように、決して悪いわけではありません。

5
Kilian Foth

従来のgotoにはさまざまな形式があり、さまざまな制限があります。 returntry{} catch{}throwbreak、またはcontinue。それらはすべて実際にgotoであり、周囲にいくつかの追加ビットがあります。

continueまたはbreakをreturnで別のループスタイルにリファクタリングできます。

for (int i = 0; i <= max; i++) {
  if(someTest) { continue; }
  if(someOtherTest) { continue; }
  doStuff;
}

次のように書くことができます:

for (int i = 0; i <= max; i++) {
  func();
}
....

void func() {
  if(someTest) { return; }
  if(someOtherTest) { return; }
  doStuff;
}

どちらが読みやすいですか?ええと、私は最初のものはループのコンテキストが失われるからだと主張しがちですが、それ以外は同じです。日常のコードでは、人々がcontinuereturnよりも少なく見える傾向があるというだけのことです。

から Javaチュートリアル:分岐ステートメントbreakおよびcontinueをラベルとともに使用すると、ほとんどの人が理解するよりも少し強力になります。また、whichループが制御ステートメントによって実行されていることに関して、コードを少し明示的にします。検討してください:

class ContinueWithLabelDemo {
    public static void main(String[] args) {
        String searchMe = "Look for a substring in me";
        String substring = "sub";
        boolean foundIt = false;

        int max = searchMe.length() -  substring.length();

    test:
        for (int i = 0; i <= max; i++) {
            int n = substring.length();
            int j = i;
            int k = 0;
            while (n-- != 0) {
                if (searchMe.charAt(j++) != substring.charAt(k++)) {
                    continue test;
                }
            }
            foundIt = true;
            break test;
        }
        System.out.println(foundIt ? "Found it" : "Didn't find it");
    }
}

continuebreakは、どのループに続くか、またはどのループにブレークするかを指定するラベルと共に使用されることに注意してください(breakのラベルは、1レベルしかないため、必要ありません。 、ただしコードの理解に役立ちます)。

本当にしたい場合は、このコードをできます

public class Demo {
    public static void main(String[] args) {
        String searchMe = "Look for a substring in me";
        String substring = "sub";
        boolean foundIt;

        int max = searchMe.length() - substring.length();

        foundIt = loop2(searchMe, substring, max);
        System.out.println(foundIt ? "Found it" : "Didn't find it");
    }

    private static boolean loop2(String searchMe, String substring, int max) {
        for (int i = 0; i <= max; i++) {
            if (loop(searchMe, substring, i)) {
                return true;
            }
        }
        return false;
    }

    private static boolean loop(String searchMe, String substring, int j) {
        int n = substring.length();
        int k = 0;
        while (n-- != 0) {
            if (searchMe.charAt(j++) != substring.charAt(k++)) {
                return false;
            }
        }
        return true;
    }
}

確かにそこには不平を言うためのビットがありますが、私はそのリファクタリングを正しく行ったと思います)...しかし、それがあなたに何をもたらすのか本当にわかりません。そして、複数のリターンを持つこれらの2つのメソッドは、「複数のリターンはアンチパターン」群衆(continuebreakがアンチパターン群衆と同じくらい有効なポイントを持つ)の怒りを引きます。 -そして上記のケースでは、おそらくより正当化された怒りも)。はい、これは、他のテストを利用して " 矢印は反パターン "の人々をあなたに面白く見せるように書くこともできます。

continueはループ内のガードステートメントです。 breakは初期のリターンです。

あまり使われていないからといって、それがアンチパターンになるわけではありません。それは単に人々がそれに慣れていないことを意味します。


余談ですが、gotoC# でカムバックのビットを見ることができます- 制限あり (C#にはbreakのようにJavaのようにラベルが付けられていないため、メソッドを抽出する必要がないため、これはコードを書く方法)。

4
user40980