2

このようなクラスのペアがあります。

public abstract class Class1 {

//...

    public Class1() {
        //...
        function2();
        //...
    }

    protected abstract void function2();

}

public class Class2 implements Class1 {

    private final OnSomethingListener mOnSomethingListener = new OnSomethingListener() {
        @Override
        onSomething() {
            doThatOtherThing();
        }
    }

    protected void function2() {
        //uses mOnSomethingListener
        //however mOnSomethingListener is null when this function is called from super()
        //...
    }

    public Class2() {
        super();
    }
}

super()実質的に参照していて、まだインスタンス化されていないため、リスナーは null であると想定しています。finalでも、まあ、あるから作りたい。このフィールド (リスナー) をスーパークラス (リスナーを使用しない) に入れずに、時間内に初期化することはできますか?

4

3 に答える 3

5

あなたのデザインは「リークthisされた問題」のインスタンスであり、Java のアンチパターンです。パブリックにオーバーライド可能なメソッドをコンストラクターから呼び出してはなりません。

于 2012-06-29T11:54:31.727 に答える
3

短い答え - いいえ。スーパークラスのコンストラクター、フィールド初期化子、およびインスタンス初期化子は、常にサブクラスの前に呼び出されます。

呼び出しの順序は、JLS のセクション 8.8.7.1 で正式に定義されています。関連する最後の部分を要約します (Sはスーパークラスで、Cはサブクラスです):

S (存在する場合) に関して i のすぐ外側のインスタンスを決定した後、スーパークラス コンストラクター呼び出しステートメントの評価は、通常のメソッド呼び出しと同様に、コンストラクターへの引数を左から右に評価することによって続行されます。そして、コンストラクターを呼び出します。

最後に、スーパークラス コンストラクター呼び出しステートメントが正常に完了すると、C のすべてのインスタンス変数初期化子と C のすべてのインスタンス初期化子が実行されます。インスタンス初期化子またはインスタンス変数初期化子 I がテキスト上で別のインスタンス初期化子またはインスタンス変数初期化子 J の前にある場合、I は J の前に実行されます。

そのため、スーパークラス コンストラクターが実行されると、サブクラスとそのすべてのフィールドは完全に初期化されません。このため、オーバーライドされたメソッドをコンストラクターから呼び出すことはお勧めできません。基本的に、オブジェクトへの参照をコンストラクターから「エスケープ」させています。つまり、構築のすべての保証がオフになっています (値を変更する final フィールドなどを含む)。

コンストラクターから抽象メソッドを呼び出すことは、ほとんどの場合間違っています。サブクラスでのメソッドの実装によっては、場合によっては (つまり、メソッドがまったく状態に依存しない場合) うまくいくかもしれませんが、ほとんどの場合、デバッグが困難な障害が発生します。ある時点で。

たとえば、次のような違いがあると思いますか?

protected String function2() {
    return "foo";
}

private final String foo = "foo";

protected String function2() {
    return foo;
}

このような問題を分析するのは困難です。クラスがどのように機能するかというメンタル モデルを壊してしまうからです。この状況を完全に回避するのが最善です。

于 2012-06-29T11:50:29.100 に答える
2

スーパークラスは常にサブクラスの前に初期化されます。最初に初期化できるのは、サブクラスの静的フィールドのみです。

初期化されていないフィールドにアクセスするスーパークラスからオーバーライドされたメソッドを呼び出すことができます。これは悪い習慣と見なされます。

于 2012-06-29T11:51:00.053 に答える