web-dev-qa-db-ja.com

到達不能なコードが正常に動作する-方法は?

私が書いた次のコードの理解から、ステートメント「I am unreachable」returnの後にコンパイルされるべきではありません。

ただし、完全にコンパイルできます。

また、JLSから: nreachable Statements コンパイルしないでください。

14.21の到達不能ステートメントの仕様から:

次の両方に該当する場合、tryステートメントは正常に完了できます。

  • Tryブロックは正常に完了できますまたはcatchブロックは正常に完了できます。

  • Tryステートメントにfinallyブロックがある場合、finallyブロックは正常に完了できます。

ここでは、tryブロックは正常に完了できませんが、catchブロックはfinallyブロックと同様にできるため、ここで混乱しています

    public class Test1 {
     public static void main(String[] args) {
        try {
            return;

        } catch (Exception e) {
            System.out.println("catch");

        } finally {
            System.out.println("finally");
        }
        System.out.println("I am unreachable??!!!");
    }
}

誰かがこの行動を理解するのを手伝ってくれますか?

57
Show Stopper

これらは JLS 14.21 からの関連する引用だと思います。

  • Switchブロックではない空のブロックは、到達可能であれば正常に完了できます。

    Switchブロックではない空でないブロックは、その中の最後のステートメントが正常に完了できる場合に限り、正常に完了できます。

    Switchブロックではない空でないブロックの最初のステートメントは、ブロックが到達可能であれば到達可能です。

    switchブロックではない空でないブロック内の他のステートメントSは、Sの前のステートメントが正常に完了できる場合にのみ到達可能です

あなたの

System.out.println("I am unreachable??!!!");

ステートメントは到達可能であれば(つまり、 "if and only if"を意味する)、tryステートメントは正常に完了し、次の引用につながります。

  • Tryステートメントは、以下の両方が真である場合にのみ正常に完了できます。

    • Tryブロックは正常に完了するか、catchブロックは通常に完了することができます。

    • tryステートメントにfinallyブロックがある場合、finallyブロックは正常に完了します

catchブロックは正常に完了することができるためおよび正常に完了することができるfinallyブロックがあるため、tryステートメントは正常に完了することができます。したがって、tryブロック内のreturn;ステートメントに関係なく、それに続くSystem.out.println("I am unreachable??!!!");ステートメントは到達可能と見なされます。

orに注意してください

Tryブロックは正常に完了できますまたは任意のcatchブロックが正常に完了できます。

これには、tryブロックまたはの少なくとも1つが正常に完了するcatchブロックのいずれかが必要です。 tryブロックとcatchブロックの両方を正常に完了する必要はありません。

最後に、この動作の背後にあるロジック:

コンパイラーは、tryブロックがExceptionをスローできるかどうかを分析することを想定していません。その理由は、Exceptionクラス階層にはチェック済み例外と未チェック例外の両方が含まれており、未チェック例外はthrows句で宣言されていないためです(ExceptionIOExceptionなどのチェック済み例外に置き換えた場合、コンパイラーはtryブロックがその例外をスローしないと文句を言うでしょう、これによりcatchブロックに到達できなくなります)。

したがって、正常に完了できるcatch (Exception e)ブロックがあるため、コンパイラーは、このcatchブロックが到達可能であると想定します。したがって、tryブロックが正常に完了できなくても、tryステートメント全体が正常に完了することができます。

finallyブロックも実行されるため、finallyブロックが存在する場合は正常に完了できる必要があります。したがって、正常に完了できなかった場合、tryステートメント全体が正常に完了できませんでした。

71
Eran

試してみてください。

例外があり、それが直接キャッチされる場合はどうなりますか。したがって、コンパイラの観点からは到達不能ではなく、正常にコンパイルされます。

キャッチで復帰する場合もコンパイルは失敗します

また、 JLS 14.21

到達可能なbreakステートメントは、breakターゲット内に、tryブロックにbreakステートメントが含まれるtryステートメントがないか、tryブロックにbreakステートメントが含まれ、それらのtryステートメントのすべてのfinally節が完了できるtryステートメントがある場合にステートメントを終了します通常は。

Tryとcatchの両方に戻った場合、以下の出力を参照してください。

jshell>  public class Test1 {
   ...>     public static void main(String[] args) {
   ...>         try {
   ...>             return;
   ...>
   ...>         } catch (Exception e) {
   ...>             return;
   ...>
   ...>         }
   ...>
   ...>         System.out.println("I am unreachable??!!!");
   ...>     }
   ...> }
|  Error:
|  unreachable statement
|          System.out.println("I am unreachable??!!!");
|          ^------------------------------------------^

Finallyステートメントでreturnがあり、コンパイルが失敗する場合も同様です。

試行後のステートメントは、次の場合に到達可能と見なされます。

1) Try has a return statement with catch and finally not having return statement
2) Try does not have a return statement with catch having or not having return statement and finally not having return statement
3) Try, catch and finally not having return statement
15
Aman Chhabra

問題のより単純な理由を与えようとすると、tryブロックで例外が発生した場合にコードに到達できます。その場合、制御はさらにブロックをキャッチし、最後にブロックをキャッチします。最終的にブロックした後、特定のステートメントが実行されます。

try {
            return;                                 //line 1

        } catch (Exception e) {
            System.out.println("catch");            //line 2

        } finally {
            System.out.println("finally");          //line 3
        }
        System.out.println("I am unreachable??!!"); //line 4

つまり、2つのケースがあるため、2つのフローがあります。

  1. 行1->行3->戻り(例外がない場合)
  2. 1行目(例外が発生する)-> 2行目-> 3行目-> 4行目(例外が発生した場合)

ラインが到達不能になるのは、コントロールがそこに行く可能性を残さない場合のみです。そのための2つの方法があります。

  1. catchブロックから戻る
  2. finallyブロックから戻ります。

どちらの場合も、コントロールはその行に決して流れません。

try {
            return;                                 //line 1

        } catch (Exception e) {
            System.out.println("catch");            //line 2
            return;                                 //return control
        } finally {
            System.out.println("finally");          //line 3
            return;                                 //or return from here
        }
        System.out.println("I am unreachable??!!"); //line 4    

これで、問題の実際の理由を明確に把握できるようになりました。

10

Javaプログラムの「到達不能なステートメント」を見ると、重要なのは言語の定義が言っていることであり、賢いコンパイラーが見つけることができるものではありません。

Java言語によると、最後のprintlnは到達不能なステートメントではありません。コードを見ても、実行できないことを理解するのは(賢い人間にとって)簡単です。

プログラミング言語は、コンパイラーが簡単に正確に従うことができる固定ルールに依存する必要があります。コンパイラーは賢さに依存できません。なぜなら、コンパイラーごとに賢さの量が異なるため、単純で固定されたルールに従わなかった場合、一部のコンパイラーはステートメントに到達できず、一部のコンパイラーが到達できなくなるためです。

4
gnasher729