86

プログラムをデバッグするとき、コードのブロック内にreturnステートメントを挿入するのが便利であることがよくあります(おそらく悪い習慣ですが)。私はJavaでこのようなことを試みるかもしれません...。

class Test {
        public static void main(String args[]) {
                System.out.println("hello world");
                return;
                System.out.println("i think this line might cause a problem");
        }
}

もちろん、これによりコンパイラエラーが発生します。

Test.java:7:到達不能ステートメント

未使用のコードを使用することは悪い習慣であるため、警告が正当化される理由を理解できました。しかし、なぜこれがエラーを生成する必要があるのか​​理解できません。

これはただのJavaが乳母になろうとしているのでしょうか、それともこれをコンパイラエラーにする正当な理由がありますか?

4

8 に答える 8

66

到達不能コードはコンパイラにとって無意味だからです。コードを人々にとって意味のあるものにすることは、コンパイラーにとって意味のあるものにすることよりも重要で困難ですが、コンパイラーはコードの本質的な消費者です。Javaの設計者は、コンパイラにとって意味のないコードはエラーであるという見方をしています。彼らのスタンスは、到達不能コードがある場合、修正が必要な間違いを犯したというものです。

ここにも同様の質問があります:到達不能コード:エラーまたは警告?、著者は「個人的にはエラーであるべきだと強く感じています。プログラマーがコードを書く場合、それは常に何らかのシナリオで実際に実行することを意図している必要があります。」明らかに、Javaの言語設計者は同意します。

到達不能コードがコンパイルを妨げるべきかどうかは、コンセンサスが得られない問題です。しかし、これがJava設計者がそれを行った理由です。


コメントの多くの人々は、Javaがコンパイルを妨げない到達不能コードの多くのクラスがあることを指摘しています。ゲーデルの結果を正しく理解していれば、到達不能コードのすべてのクラスをキャッチできるコンパイラはないでしょう。

単体テストでは、すべてのバグを検出できるわけではありません。私たちはこれを彼らの価値に対する議論として使用しません。同様に、コンパイラは問題のあるコードをすべてキャッチすることはできませんが、可能であれば不正なコードのコンパイルを防ぐことは依然として価値があります。

Java言語設計者は、到達不能コードをエラーと見なします。したがって、可能な場合はコンパイルを防ぐのが妥当です。


(反対票を投じる前に:問題は、Javaに到達不能なステートメントコンパイラエラーが発生するかどうかではありません。問題は、Javaに到達不能なステートメントコンパイラエラーが発生する理由です。Javaが間違った設計上の決定をしたと思うからといって、私に反対票を投じないでください。)

于 2010-09-25T21:22:31.870 に答える
47

到達不能なステートメントを許可してはならない明確な理由はありません。他の言語では問題なく使用できます。あなたの特定の必要性のために、これは通常のトリックです:

if (true) return;

それは無意味に見えます。コードを読む人は誰でも、それが意図的に行われたに違いないと推測します。残りのステートメントを到達不能のままにするという不注意な間違いではありません。

Javaは「条件付きコンパイル」を少しサポートしています

http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.21

if (false) { x=3; }

コンパイル時エラーは発生しません。最適化コンパイラは、ステートメントx=3を認識する場合があります。実行されることはなく、生成されたクラスファイルからそのステートメントのコードを省略することを選択できますが、ステートメントx = 3; ここで指定された技術的な意味で「到達不能」とは見なされません。

この異なる扱いの理論的根拠は、プログラマーが次のような「フラグ変数」を定義できるようにすることです。

static final boolean DEBUG = false;

次に、次のようなコードを記述します。

if (DEBUG) { x=3; }

アイデアは、DEBUGの値をfalseからtrueまたはtrueからfalseに変更してから、プログラムテキストに他の変更を加えることなくコードを正しくコンパイルできるようにすることです。

于 2010-09-25T22:47:16.253 に答える
19

ナニーです。.Netはこれを正しく理解したと思います。到達不能コードに対して警告が表示されますが、エラーは発生しません。警告されるのは良いことですが、コンパイルを妨げる理由はありません(特に、一部のコードをバイパスするためにリターンをスローするのが良いデバッグセッション中)。

于 2010-09-25T21:35:52.717 に答える
14

私はこの質問に気づいただけで、これに$.02を追加したいと思いました。

Javaの場合、これは実際にはオプションではありません。「到達不能コード」エラーは、JVM開発者が開発者を何かから保護したり、特に警戒したりすることを考えたという事実からではなく、JVM仕様の要件から発生します。

JavaコンパイラとJVMはどちらも、「スタックマップ」と呼ばれるものを使用します。これは、現在のメソッドに割り当てられた、スタック上のすべてのアイテムに関する明確な情報です。JVM命令が、あるタイプのアイテムを別のタイプに誤用しないように、スタックのすべてのスロットのタイプを知っている必要があります。これは、数値がポインタとして使用されるのを防ぐために最も重要です。Javaアセンブリを使用して、数値をプッシュ/保存しようとしますが、オブジェクト参照をポップ/ロードすることは可能です。ただし、JVMは、クラスの検証中、つまりスタックマップが作成され、整合性がテストされているときに、このコードを拒否します。

スタックマップを検証するには、VMはメソッドに存在するすべてのコードパスをウォークスルーし、どのコードパスが実行されるかに関係なく、すべての命令のスタックデータが以前のコードがプッシュしたものと一致することを確認する必要があります/スタックに保存されます。したがって、次の単純な場合:

Object a;
if (something) { a = new Object(); } else { a = new String(); }
System.out.println(a);

3行目で、JVMは'if'の両方のブランチがObjectと互換性のあるもの(ローカルvar#0)にのみ格納されていることを確認します(3行目以降のコードがローカルvar#0を処理する方法であるため) )。

コンパイラが到達不能コードに到達すると、その時点でスタックがどのような状態にあるかが完全にはわからないため、その状態を検証できません。ローカル変数も追跡できないため、その時点でコードを完全にコンパイルすることはできません。そのため、クラスファイルにこのあいまいさを残す代わりに、致命的なエラーが発生します。

もちろん、のような単純な条件if (1<2)はそれをだますでしょうが、それは実際にはだまされていません-それはコードにつながる可能性のある分岐を与えており、少なくともコンパイラとVMの両方がそこからスタックアイテムをどのように使用できるかを決定できますの上。

PSこの場合、.NETが何をするのかわかりませんが、コンパイルも失敗すると思います。これは通常、マシンコードコンパイラ(C、C ++、Obj-Cなど)では問題になりません。

于 2013-08-01T21:48:37.733 に答える
5

このコンパイラエラーは良いことだと思いますが、回避する方法があります。真になることがわかっている条件を使用します。

public void myMethod(){

    someCodeHere();

    if(1 < 2) return; // compiler isn't smart enough to complain about this

    moreCodeHere();

}

コンパイラはそれについて文句を言うほど賢くはありません。

于 2010-09-25T21:36:57.987 に答える
5

コンパイラの目標の1つは、エラーのクラスを除外することです。到達不能コードが偶然に存在します。javacがコンパイル時にそのクラスのエラーを除外するのは素晴らしいことです。

誤ったコードをキャッチするすべてのルールについて、誰かがコンパイラーにそれを受け入れてもらいたいと思うでしょう。なぜなら、彼らは自分たちが何をしているのかを知っているからです。これはコンパイラチェックのペナルティであり、バランスを正しくとることは言語設計のトリッキーなポイントの1つです。厳密にチェックしても、作成できるプログラムの数は無限にあるので、それほど悪くはありません。

于 2010-09-25T22:02:16.433 に答える
0

許可する理由がフラグを許可することである場合、コンパイラはフラグではない(変数ではない)ことを「認識している」ため、コンパイル時エラーを生成しないif (aBooleanVariable) return; someMoreCode;という事実は、if (true) return; someMoreCode;CodeNotReachable例外を生成するポリシーに矛盾があるように見えます。true

興味深いかもしれない他の2つの方法ですが、メソッドのコードの一部をオフにすることには適用されませんif (true) return

さて、言う代わりに、jvm引数に言って追加しif (true) return;たいかもしれません。良い点は、これによりある程度の粒度が可能になり、jvm呼び出しに追加のパラメーターを追加する必要があるため、コードにDEBUGフラグを設定する必要はありませんが、実行時に引数を追加することで、ターゲットがターゲットでない場合に役立ちます。開発者マシンとバイトコードの再コンパイルと転送には時間がかかります。assert false-ea OR -ea package OR -ea className

方法もありますがSystem.exit(0)、これはやり過ぎかもしれません。JSPのJavaに配置すると、サーバーが終了します。

Javaは設計上「乳母」言語であるということは別として、制御を強化するためにC /C++などのネイティブなものを使用したいと思います。

于 2012-06-08T21:50:18.130 に答える
0

必要なことを実行できる限り、コンパイラが厳密であるほど優れていると文句を言うのは確かに良いことです。通常、支払うべき小さな代償はコードをコメントアウトすることです。利益は、コードをコンパイルするときに機能することです。一般的な例は、テスト/デバッグがメインテストのみで短いものであることに気付くまで人々が悲鳴を上げるHaskellです。私は個人的にJavaでデバッグをほとんど行いませんが、(実際には意図的に)注意を払っていません。

于 2010-09-25T22:35:14.400 に答える