web-dev-qa-db-ja.com

Javaが配列やクラス変数などのようにローカル変数をデフォルト値で自動的に初期化しないのはなぜですか?

public class A {
    public static void main(String[] args) {
        boolean[] test = new boolean[1];
        System.out.println(test[0]);
    }
}

Java仕様 で指定されたfalseを出力します。

しかしながら、

public class A {
    public static void main(String[] args) {
        boolean test;
        System.out.println(test);
    }
}

プリント

A.Java:4: error: variable test might not have been initialized
        System.out.println(test);
                           ^
1 error

したがって、明らかに:Javaはローカルのプリミティブ変数を初期化しません-デフォルト値falseは価値がありますが。

  • しかし、型のデフォルト値が役立つかもしれないのに、なぜそうなのでしょうか?
  • 理由は何でしたか、Javaはそのように設計されましたか?
  • さらに、なぜ、より最近のJava=バージョン?(7,8,9))で変更されなかったのですか?
2
toogley

In ShortC++の後継としての言語設計の問題

(変数ではなく)フィールドは、オブジェクトのコンストラクターまたは初期化子でのみ初期化できます。 C++では、これはPoint::Point(): x(0), y(0) {}のようなコンストラクターを使用して行われたはずです。彼らはそのボイラープレートコードと: ...。したがって、オブジェクトのゼロ化。 finalフィールドの場合でも、割り当てのチェックが行われることに注意してください。

C++のローカル(プリミティブおよびポインター)変数はその時点では初期化されておらず、コンパイラーはすべてのフローが変数を初期化するかどうかを確認できます。これは、デフォルトの初期化よりもlessエラーが発生しやすくなります。たとえば、次のif-elseが2つの異なる値で変数を初期化することが期待されているが、1つのブランチで忘れられた場合。 デフォルト、特にnullがローカル変数の実用的な値になることはほとんどありません。

ローカル変数をデフォルト値で最初に初期化するコードコストは無視できます。また、制御フローがそれ自体を初期化するときに、コンパイラーはデフォルトで初期化しないことを検討することさえできます。

4
Joop Eggen

これは推測にすぎませんが、コンパイラが初期化されていない変数を検出してエラーを通知できるように、言語が定義の時点で初期化を強制しない場合、PODの最も安全な設計になる可能性があります。

コンパイル時のエラーは実行時に意外に好ましいものであり、falsebooleanのようなデフォルト値に初期化すると、プログラマーが実際には望まないものになる可能性があります-変​​数の初期化少なくとも確定的な動作を提供するため、Cのようにガベージよりもデフォルト値を使用することをお勧めします。

とにかく、それが私の最良の推測です。たとえば、プログラマがbooleantrueに設定しようとしたが、初期化するのを忘れた場合に事故が発生するのを防ぐためです。言語設計者は、デフォルトをfalseにする代わりに、エラーにすることにしました。設計者は、意図したものと一致しない可能性のある暗黙のデフォルト動作ではなく、プログラマに明示的に要求することを決定しました。

さらにムーア、なぜそれが後でより現代的に変更されなかったのかJavaバージョン?(7,8,9)?

この方法で開始し、上記の理論的根拠が言語の設計者と一致する場合、以前はエラーであったものを実行可能なランタイム動作に変換するのは安全な変更ではありません。突然、古いコードの以前のエラーであったはずのものが、ビルドしないコードからコンパイラーが突然実行できるコードに指摘できるところでエラーなしでパスするようになりました。これは、プログラマーが当初意図していたものとは異なりました。言語設計の観点から、それがこの方法で始まった場合は、この方法で無期限に保持する必要があります。

しかし、それは逆ではないでしょうか?プログラムが合法的でなかった(つまり、コンパイラエラーが発生した、または不特定の動作が呼び出された)場合、言語設計者がこのプログラムに何らかの意味を割り当てても安全です。

この質問をコメントに追加したかったのは、本当によく考えさせられたとても良い質問だからです。私は強い「ノー」を提案しますが、注意深い説明が必要です。その要点は、プログラマーが意図を間違って指定できなかった元の動作を以前は考慮していた言語またはライブラリに、暗黙の機能を自信を持って導入できないことです。これは、代わりに挿入する暗黙の動作を想定できないためです。以前の過ちは彼の当初の不特定の意図と一致していた。 boolean xのデフォルト値がfalseになる仕様で最初に作成された言語は問題ありませんが、そのような仕様がないため、ゲームの後半では、突然読むことができないと判断できません。人々の心。

これらのビルドエラーは制限の結果ではなく、プログラマーが実際の人的エラーの意図を特定できないことを意図的に言語設計者が検討した結果です。これは、可能な限りすべてのケースで恐ろしい副作用を保証できないような方法で、歴史的な設計の根拠を裏付けて、言語でそのようなしっかりとした設計決定に戻ることができるタイプのケースではありません。この場合、デフォルトの暗黙的な動作を提供することは言語機能を拡張するものではなく、型システムの中心にある基本的な設計決定を元に戻します。成熟した言語またはライブラリで基本的な設計決定を元に戻すことは、通常、実行に対して強く誤ります。

私は偏執的で厳格すぎる/独断的であるように思われるかもしれませんが、それは多くの場合、何年にもわたって世界中の人々によって作成された無数のコード行を含む成熟した言語またはライブラリを維持するための要件です。以前に決定されたエラーを非エラーに変えるだけであっても、その言語/ライブラリ用にこれまでに作成されたすべてのコードの動作を変更するような方法で、基本的な設計決定に突然戻ることはできません。これは言語の拡張ではなく、根本的な設計変更であり、絶対に必要な場合を除いて、ほとんどの場合はno-noであり、PODに暗黙のデフォルト値を提供することは、Java彼らの日を乗り切る開発者。

たとえば、以前のコードにboolean xがあり、それを使用しようとした場合、それを誤って処理する言語から、プログラマがxを意図したものであると想定することはできません。 false、前の設計以来、彼はその振る舞いを決して指定しないようにコードを書きました。彼はxtrueにしたかったかもしれませんが、実行に失敗したコードの代わりに、コードが実行され、この原因を突き止めるのが難しい散発的なバグを引き起こす可能性があります(単に実行に失敗するコードよりはるかに悪い)。

問題をより明確に示す類推的な例として、現在Javaは配列に負の数でインデックスを付けることを許可していません(一部の言語ではこれを提供し、循環インデックスを許可しています)。プログラマーのミスやエラーが発生すると、システムは範囲外の例外をスローします。ただし、言語デザイナーが突然気が変わってプレーンな古い配列の負のインデックス付けを決定した場合は、許可する必要があります。負のインデックスが付けられた配列が最初から機能することは決してありませんでした。今度は、実行時に捕捉された可能性のあるプログラマーの恐ろしい間違いを、これで大丈夫であるという前提で、間違いではないものに変えたかもしれません。実際の最終結果は、以前のJavaバージョンが負のインデックスをサポートする前に作成されたコードに対して導入された非常に致命的なバグをいくつか隠し、隠しました。

2
user204677

値の初期化に失敗すると、プログラマー側の論理エラーとなり、できるだけ早くキャッ​​チする必要があります。 Javaこれは、ローカルではコンパイル時に行われますが、フィールドでは実行時のみ(またはまったく行われません)です。

質問「なぜnotフィールドに値を提供しないエラーですか?」次に適切です。ローカル変数の場合、すべての可能なコードパスに使用前の初期化が含まれていることを簡単に確認できます。この分析で保守的であることは、プログラマーにとって過度に面倒ではありません。分析だけを超えている構造を拒否します。

{
    bool toggle = false;
    String relevant;
    if (toggle) {
        relevant = "True path";
    } else if (!toggle) {
        relevant = "False path";
    }
    // No else! but we don't need one...
    System.out.printLn(relevant);
}

は、単一のメソッドのレベルで支払うための小さな価格ですが、クラスのレベルでは、言語の使用をやめさせます。

class Example {
    Example() {}
    Example(String r) { relevant = r; }
    String relevant;
    private void TruePath() {
        relevant = "True path";
    }
    private void FalsePath() {
        relevant = "False path";
    }
    public void DoStuff(bool toggle) {
        if (toggle) {
            TruePath();
        } else if (!toggle) {
            FalsePath();
        }
        // Is relevant always initialised?
        // How should the compiler know?
        System.out.printLn(relevant);
    }
}
1
Caleth