web-dev-qa-db-ja.com

コンパイラエラーのない複数のreturnステートメント

これはインタビューの質問でした:

public class Demo {

    public static void main(String[] args) {
        System.out.println(foo());
    }

    static String foo() {
        try {
            return "try ...";
        } catch (Exception e) {
            return "catch ...";
        } finally {
            return "finally ..."; //got as result
        }
    }
}

私の質問は、コンパイル時のエラーがない理由です。 finallyブロックにreturnステートメントがある場合、finallyおよびtryブロックの代わりにcatchから戻るようにバインドされています。このコードを-Xlintオプションを使用してコンパイルしようとしたところ、警告が表示されました。

warning: [finally] finally clause cannot complete normally
64
user2575725

Java言語仕様で許可されているため、コンパイルエラーは発生しません。ただし、returnブロックにfinallyステートメントを含めることは通常悪い考えであるため、警告メッセージが表示されます。

あなたの例で何が起こるかは次のとおりです。 returnブロックのtryステートメントが実行されます。ただし、finallyブロックは常に実行する必要があるため、catchブロックの終了後に実行されます。そこで発生するreturnステートメントは、前のreturnステートメントの結果を上書きするため、メソッドは2番目の結果を返します。

同様に、finallyブロックは通常、例外をスローしません。そのため、finallyブロックは正常に、つまりreturnなしで、または例外をスローせずに完了する必要があると警告が示しています。

77
Hoopje

これはJava言語仕様で説明されています:

§14.17

finally句が突然完了すると、returnステートメントによって開始された制御の移行が中断される可能性があります。

§14.20.2

tryブロックの実行が正常に完了した場合、finallyブロックが実行され、次に選択肢があります。

  • finallyブロックが正常に完了すると、tryステートメントは正常に完了します。
  • finallyブロックが理由Sで突然完了した場合、tryステートメントは理由Sで突然完了します。

tryブロックの実行が他の理由Rで突然完了した場合、finallyブロックが実行され、次に選択肢があります。

  • finallyブロックが正常に完了した場合、tryステートメントは理由Rのために突然完了します。
  • 理由Sのためにfinallyブロックが突然完了すると、理由Sのためにtryステートメントが突然完了する(そして理由Rが破棄される)。
39

実際にはreturnステートメントの1つだけが実際にコントロールを呼び出し元のコードに返すため、コンパイル時のエラーは発生しません。

@Hoopjeで説明したように、returnまたはtry内のcatchが最初に実行され、それぞれのreturnステートメントも実行されます。ただし、コントロールを呼び出しコードに戻す直前に、finallyブロックを実行します。現在、これはblockreturnsなので、この戻り値は以前の戻り値をオーバーライドします。

10
Aakash

すばらしい質問..私の知識によれば、try and catchブロックのreturnステートメントは、finallyブロックをコードに追加した場合、finallyに転送されます。それがその働きです。

したがって、この場合はすべてのコード行が実行中であり、デバッグを試すことができます。コードの下で試した3つのブロックすべて。

public class Main {

    public static void main(String[] args) {
        System.out.println(foo());
    }
    static String foo() {
        try {
            throw new Exception();
        } catch (Exception e) {
            return "catch ...";
        } finally {
            return "finally ..."; //got as result
        }
    }
}

以下のリンクからアイデアを得ることができます。 複数の戻り値:最終戻り値を設定するものはどれですか?

8

それは基本的にこれと同じです:

public boolean someMethod(){
        if(1 == 1){
            return true;
        }
        return false;
}

警告は表示されますが、コンパイルエラーは発生しません。 no returnステートメントが実行される可能性がある場合にのみ、コンパイラーはエラーを出します。

8

Try、catch、finallyブロックにはreturnステートメントが1つしかないため、コードは正常に機能します。到達不能なreturnステートメントがあることを伝えるために、try、catch、またはfinallyブロックのいずれかで2つのreturnステートメントを書き込もうとすると、コンパイルエラーが発生します。

5
thanuja

(短い回答の場合-回答の太字と斜体の部分をお読みください)

Java 8 docsによる実行フロー。詳細を提供します。以下に基づいてreturnステートメントの実行を推測できます。

Finallyブロックを含むtryステートメントは、最初にtryブロックを実行することによって実行されます。

次に選択肢があります:

•tryブロックの実行が正常に完了すると、finallyブロックが実行され、次に選択肢があります。

– finallyブロックが正常に完了すると、tryステートメントが正常に完了します。

– finallyブロックが理由Sで突然完了した場合、tryステートメントは理由Sで突然完了します。

•値Vがスローされたためにtryブロックの実行が突然完了した場合は、選択肢があります。

– Vの実行時の型が、tryステートメントのいずれかのcatch句のキャッチ可能な例外クラスと割り当て互換である場合、最初(左端)のそのようなcatch句が選択されます。値Vが選択されたcatch節のパラメーターに割り当てられ、そのcatch節のBlockが実行されます。

次に選択肢があります:

› catchブロックが正常に完了すると、finallyブロックが実行されます。次に選択肢があります:

"finallyブロックが正常に完了すると、tryステートメントが正常に完了します。

"finallyブロックが何らかの理由で突然完了した場合、tryステートメントは同じ理由で突然完了します。

理由Rのためにcatchブロックが突然完了すると、finallyブロックが実行されます。次に選択肢があります:

"finallyブロックが正常に完了すると、理由Rのためにtryステートメントが突然完了します。

"finallyブロックが理由Sで突然完了した場合、tryステートメントは理由Sで突然完了します(理由Rは破棄されます)。

– Vのランタイムタイプが、tryステートメントのいずれかのcatch句のキャッチ可能な例外クラスとの割り当て互換性がない場合、finallyブロックが実行されます。

次に選択肢があります:

› finallyブロックが正常に完了すると、値Vがスローされたため、tryステートメントが突然完了します。

› finallyブロックが理由Sで突然完了した場合、tryステートメントは理由Sで突然完了します(値Vのスローは破棄され、忘れられます)。

•tryブロックの実行が他の理由Rで突然完了した場合、finallyブロックが実行され、次に選択肢があります。

– finallyブロックが正常に完了すると、理由Rのためにtryステートメントが突然完了します。

– finallyブロックが理由Sで突然完了した場合、tryステートメントは理由Sで突然完了します(理由Rは破棄されます)。

説明はこのリンクで明確です- javaDoc

4
Tejus Prasad

これを実行してみてください:

1、2、3が出力され、ゼロ除算例外がスローされます。

public class Demo {
  public static void main(String[] args) {
    System.out.println(foo());
  }
  public static String print(int a){
    System.out.println(a);
    return String.valueOf(a/0);
  }
  static String foo() {
    try {
      return print(1);
    } catch (Exception e) {
      return print(2);
    } finally {
      return  print(3);
    }
  }
}
2
Jens Timmerman