web-dev-qa-db-ja.com

なぜJavaに「到達不能なステートメント」コンパイラエラーがあるのですか?

プログラムをデバッグするとき、コードブロック内に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 Nannyになろうとしているのか、これをコンパイラエラーにする正当な理由があるのか​​?

79
Mike

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

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

到達不能なコードがコンパイルを妨げるかどうかは、コンセンサスが得られない問題です。しかし、これがJavaデザイナーがやった理由です。


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

単体テストでは、すべてのバグを見つけることはできません。これらの値に対する引数としてこれを使用しません。同様に、コンパイラーはすべての問題のあるコードをキャッチすることはできませんが、可能であれば不良コードのコンパイルを防ぐことは依然として重要です。

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


(ダウン投票する前に、質問はJavaに到達不能ステートメントコンパイラエラーがあるかどうかではありません。質問はwhy_ですJavaには到達不能なステートメントコンパイラエラーがあります。Javaが誤った設計決定を下したと思われるからといって、私に投票しないでください。)

61
SamStephens

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

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に変更し、プログラムテキストに他の変更を加えずにコードを正しくコンパイルできるようにすることです。

46
irreputable

ナニーです。 .Netはこれを正しかったと感じています-到達不能なコードに対して警告を発しますが、エラーではありません。それについて警告されるのは良いことですが、コンパイルを防ぐ理由はありません(特にデバッグセッション中にコードをバイパスするためにリターンをスローするのが良い場合)。

18
Brady Moritz

この質問に気づいただけで、これに$ .02を追加したかったのです。

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

JavaコンパイラとJVMの両方は、「スタックマップ」と呼ばれるものを使用します。これは、現在のメソッドに割り当てられたスタック上のすべてのアイテムに関する明確な情報です。 JVM命令が1つのタイプのアイテムを別のタイプに誤って処理しないように、スタックの各スロットのタイプを把握する必要があります。これは、数値がポインターとして使用されるのを防ぐために最も重要です。 Java Assemblyを使用して、数値のプッシュ/保存を試みた後、オブジェクト参照をポップ/ロードすることができます。ただし、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の両方につながる可能性のある分岐を与えます_は、スタックアイテムの使用方法を決定できます。

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

14
Pawel Veselov

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

エラーのあるコードをキャッチするすべてのルールについて、何をしているのか知っているので、誰かがコンパイラーにそれを受け入れてほしいと思うでしょう。これはコンパイラーチェックのペナルティであり、バランスを適切に保つことは、言語設計の要点の1つです。最も厳密なチェックを行っても、作成できるプログラムの数は無限であるため、それほど悪くはなりません。

5
Ricky Clarkson

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

public void myMethod(){

    someCodeHere();

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

    moreCodeHere();

}

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

5

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

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

興味深いかもしれないが、メソッドコードの一部とif (true) returnのスイッチオフには適用しない2つの方法:

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

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

それとは別に、Javaは設計上「nanny」言語であるため、C/C++のようなネイティブなものを使用して制御します。

0
MichaelS