コンパイルしようとすると
for(;;)
{
}
System.out.println("End");
JavaコンパイラはUnreachable statement
というエラーを生成します。しかし、別の "nreachable"(私によると)break
ステートメントを追加すると成功する:
for(;;)
{
if(false) break;
}
System.out.println("End");
コンパイルします。エラーが発生しないのはなぜですか?
動作は 到達不能ステートメントのJLS記述 で定義されています:
If-thenステートメントが到達可能である場合、thenステートメントは到達可能です。
したがって、コンパイラは、thenステートメント(break;
)は、if
の状態に関係なく、到達可能です。
そしてもう少し、私の強調:
基本的な
for
ステートメントは、次の少なくとも1つが当てはまる場合に正常に完了することができます。
- Forステートメントは到達可能であり、条件式があり、条件式は値がtrueの定数式(§15.28)ではありません。
- forステートメントを終了する到達可能な
break
ステートメントがあります。
したがって、thenステートメントにはbreak
が含まれているため、forは正常に完了できます。お気づきのように、break
をreturn
に置き換えても機能しません。
理論的根拠は、セクションの終わりに向かって説明されています。実質的に、if
には、次のような構成を許可する特別な処理があります。
if(DEBUG) { ... }
ここで、DEBUGはコンパイル時定数です。
同様の質問に対する私の答え で説明されているように、特定の構造if(compile-time-false)
は、明示的なバックドアとして到達不能ルールから免除されます。この場合、コンパイラはbreak
を到達可能として扱います。
から JLS
次の少なくとも1つが当てはまる場合、if-thenステートメントは正常に完了することができます。
> if-thenステートメントに到達可能であり、条件式が値がtrueの定数式ではありません。
> thenステートメントは正常に完了できます。
したがって、if(false)
が許可されます。
「条件付きでコンパイル」するこの機能は、バイナリ互換性に大きな影響を与えます。このような「フラグ」変数を使用するクラスのセットがコンパイルされ、条件付きコードが省略された場合、フラグの定義を含むクラスまたはインターフェイスの新しいバージョンだけを後で配布するだけでは不十分です。したがって、フラグの値の変更は、既存のバイナリとバイナリ互換ではありません。 (switchステートメントのcaseラベルで定数を使用するなど、このような非互換性には他の理由もあります;)
基本的に、到達不能コードは、プログラムを静的に分析することによって検出されますwithout実際にコードを実行します。条件はランタイムでチェックされます。したがって、この分析が行われるとき、実際には状態を調べませんが、break;
は、if
を介してアクセス可能(到達可能)です。
Javaがall到達不能ステートメントを検出しない主な理由は、コードが到達可能かどうかに答えることが一般的に不可能であるということですこれは、 停止問題 がチューリングマシンでは決定不可能であるという事実に起因します。
したがって、到達不能なステートメントをすべて検出できるわけではないことは明らかですが、条件を評価してみませんか?使用される条件がfalse
だけでなく、~x == x
のようなものであると想像してください。たとえば、これらのステートメントはすべて、int x
( source )ごとにtrue
を出力します。
System.out.println((x + x & 1) == 0);
System.out.println((x + -x & 1) == 0);
System.out.println((-x & 1) == (x & 1));
System.out.println(((-x ^ x) & 1) == 0);
System.out.println((x * 0x80 & 0x56) == 0);
System.out.println((x << 1 ^ 0x1765) != 0);
ステートメントはかなり複雑になる可能性があります。それらを解決するには時間がかかります。ビルド時間が大幅に増加し、結局のところ、到達不能なステートメントがすべて検出されるわけではありません。コンパイラーは、ある程度の努力を払うように設計されていますが、そのために多くの時間を費やすことはありません。
残っている唯一の問題は、条件の解決をどこで停止するかということです。その理由は数学的に正当化されていないようで、使用シナリオに基づいています。あなたの特定のケースの理論的根拠は JLS-14.21 によって与えられます