2

最終オブジェクト (以下のコードの A String) を渡すと、匿名の内部クラスから出力されると null として表示されます。ただし、最終的な値の型またはストレートの最終的な文字列が渡されると、その値は正しく表示されます。final匿名内部クラスのコンテキストで実際に意味することは何ですか?また、オブジェクトが null に渡されるのはなぜですか?

public class WeirdInners
{
    public class InnerThing
    {
        public InnerThing()
        {
            print();
        }

        public void print(){

        }
    }

    public WeirdInners()
    {
        final String aString = "argh!".toString();
        final String bString = "argh!";
        System.out.println(aString);
        System.out.println(bString);


        InnerThing inner =new InnerThing(){
            public void print()
            {
                System.out.println("inner"+aString); // When called from constructor, this value is null.
                System.out.println("inner"+bString); // This value is correctly printed.
            }
        };

        inner.print();
    }


    public static void main(String[] args)
    {
        WeirdInners test1 = new WeirdInners();
    }

}

Stringはオブジェクトであることが期待されるため、これは私にとって非常に奇妙な動作です。なぜ呼び出すとtoString()物事が変わるのでしょうか?

その他の情報: この動作は、Java 5 ではなく、Java 1.4 を使用してのみ観察されます。回避策に関する提案はありますか? 既存の Stringを呼び出さないことtoString()は十分に公平ですが、これは単なる例であるため、String 以外のオブジェクトで実行すると現実世界に影響を与えます。

4

3 に答える 3

5

JLSのセクションをチェックするcompile-time constantsと、呼び出しが違いを生むことがわかります.toString()。接頭辞のようなナンセンスもそうfalse?null+"":です。

ここで重要なのは、閉じられるフィールドとコンストラクターを設定する相対的な順序です。それ以降を使用する場合-target 1.4(1.4ではデフォルトではありません!)、スーパーを呼び出す前にフィールドがコピーされます。1.3より前の仕様では、これは不正なバイトコードでした。

これらの場合によくあることjavap -cですが、javacコンパイラが何をしているかを確認するのに役立ちます。この仕様は、理由を理解するのに役立ちます(十分な忍耐力が必要です)。

于 2009-10-28T21:20:38.120 に答える
0

this私の推測では、オブジェクトが完全に構築されていないときに、InnerThing()のコンストラクターがInnerThingの匿名サブクラスのprintメソッドに(暗黙的に)それを渡すと、未定義の動作がトリガーされます。これは、WierdInnersthisのへの暗黙の参照に依存します。this

を呼び出す.toString()と、aStringの初期化がコンパイル時から実行時に移動します。未定義の動作がJava1.4と1.5で異なる理由は、おそらくJVM実装の詳細です。

于 2009-10-28T21:21:56.557 に答える
0

オーバーライドされたメソッドは、オブジェクトのサブクラス部分が初期化される前に呼び出されるため、スーパークラス コンストラクターから呼び出すのは危険です。

さらに、外側のスコープの final 変数にアクセスする内部クラスは、実際にはそれらの変数のコピーにアクセスし (そのため、それらは final である必要があります)、これらのコピーされたフィールドは無名サブクラスに存在します。

処理が異なる理由bStringは、その値がコンパイル時にわかっているためだと思います。これにより、コンパイラはサブクラスのフィールド アクセスをインライン化できるため、フィールドが初期化される時間が無関係になります。

于 2009-10-28T21:29:07.403 に答える