web-dev-qa-db-ja.com

Javaの初期化されていない変数とメンバー

このことを考慮:

public class TestClass {

    private String a;
    private String b;

    public TestClass()
    {
    a = "initialized";
    }

    public void doSomething()
    {
    String c;

        a.notify(); // This is fine
    b.notify(); // This is fine - but will end in an exception
    c.notify(); // "Local variable c may not have been initialised"
    }

}

わかりません。 「b」は初期化されませんが、「c」と同じ実行時エラーが発生します。これはコンパイル時エラーです。なぜローカル変数とメンバーの違いがあるのですか?

編集:メンバーを非公開にすることが私の最初の意図でしたが、疑問はまだ残っています...

27
Yuval Adam

明確な割り当てのルールは非常に困難です(JLS第3版の第16章を読んでください)。フィールドに明確な割り当てを強制することは実用的ではありません。現状では、初期化される前に最終フィールドを観察することさえ可能です。

13

言語はそれをこのように定義します。

オブジェクトタイプのインスタンス変数は、デフォルトでnullに初期化されます。オブジェクトタイプのローカル変数はデフォルトでは初期化されておらず、未定義の変数にアクセスするのはコンパイル時エラーです。

ここのセクション4.5.5を参照してください http://docs.Oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5

44
jcoder

これが取引です。あなたが電話するとき

TestClass tc = new TestClass();

newコマンドは、次の4つの重要なタスクを実行します。

  1. 新しいオブジェクトのヒープにメモリを割り当てます。
  2. クラスフィールドをデフォルト値(数値は0、ブール値はfalse、オブジェクトはnull)に開始します。
  3. コンストラクターを呼び出します(フィールドを再開始する場合としない場合があります)。
  4. 新しいオブジェクトへの参照を返します。

したがって、フィールド「a」と「b」は両方ともnullで開始され、「a」はコンストラクターで再開始されます。このプロセスはメソッド呼び出しには関係ないため、ローカル変数 'c​​'は決して初期化されません。

HTH

PS:深刻な不眠症については、 this を読んでください。

23
Yuval

コンパイラは、cが設定されないことを理解できます。 b変数は、コンストラクターが呼び出された後、doSomething()の前に他の誰かによって設定される可能性があります。 bをプライベートにすると、コンパイラが役立つ場合があります。

3
user35094

コンパイラーは、doSomething()のコードから、cがそこで宣言されており、初期化されていないことを知ることができます。ローカルであるため、他の場所で初期化される可能性はありません。

DoSomething()をいつどこで呼び出すかはわかりません。 bはパブリックメンバーです。メソッドを呼び出す前に、他のコードで初期化することは完全に可能です。

3
Dave Costa

メンバー変数は、nullに初期化されるか、プリミティブの場合はデフォルトのプリミティブ値に初期化されます。

ローカル変数は未定義で初期化されていないため、初期値を設定する必要があります。コンパイラはそれらを使用できないようにします。

したがって、cが未定義のときにクラスTestClassがインスタンス化されると、bが初期化されます。

注:nullはundefinedとは異なります。

2
dpp

受け入れられた答えが言ったように、bが初期化されているかどうかを見分けるのは難しいので、実行時ではなく編集/コンパイル時にエラーを見つけようとするJavaのシステムの大きな穴の1つを実際に特定しました。

この欠陥を回避するためのいくつかのパターンがあります。 1つ目は「デフォルトで最終」です。メンバーがファイナルの場合は、コンストラクターでメンバーを入力する必要があります。パス分析を使用して、可能なすべてのパスがファイナルに入力されるようにします(「Null」を割り当てることもできますが、目的は無効になりますが、少なくとも、意図的に行っていることを認識せざるを得ないでしょう)。

2番目のアプローチは、厳密なnullチェックです。プロジェクトまたはデフォルトのプロパティのいずれかでEclipse設定でオンにできます。呼び出す前にb.notify()をnullチェックする必要があると思います。これはすぐに手に負えなくなる可能性があるため、物事を簡単にするために一連の注釈を付ける傾向があります。

アノテーションの名前は異なる場合がありますが、概念的には、厳密なnullチェックとアノテーションをオンにすると、変数のタイプは「nullable」と「NotNull」になります。 Nullableをnull以外の変数に配置しようとする場合は、最初にnullをチェックする必要があります。パラメータと戻り値の型にも注釈が付けられているため、null以外の変数に割り当てるたびにnullをチェックする必要はありません。

また、「NotNullByDefault」パッケージレベルのアノテーションがあり、Nullableにタグを付けない限り、変数がnull値を持つことはできないとエディターに想定させます。

これらのアノテーションは主にエディターレベルで適用されます(Eclipseやおそらく他のエディター内でオンにできます)。そのため、必ずしも標準化されているとは限りません。 (少なくとも前回チェックしたとき、Java 8にはまだ見つけていない注釈があるかもしれません)

1
Bill K