8

予期しない動作の理由を突き止めるのに 1 時間以上費やしたに違いありません。フィールドが期待どおりに設定されていないことに気付きました。肩をすくめて先に進む前に、なぜこれがこのように機能するのかを理解したいと思います。

以下の例を実行すると、出力が true になると思いますが、false です。他のテストでは、そのタイプのデフォルト値が何であれ、常に取得することが示されています。

public class ClassOne {

    public ClassOne(){
        fireMethod();
    }

    protected void fireMethod(){
    }

}

public class ClassTwo extends ClassOne {

    boolean bool = true;

    public ClassTwo() {
        super();
    }

    @Override
    protected void fireMethod(){
        System.out.println("bool="+bool);
    }

    public static void main(String[] args) {
        new ClassTwo();
    }
}

出力:

bool=false
4

5 に答える 5

5

スーパークラス コンストラクターは、サブクラス コンストラクターの前に呼び出されます。Java では、コンストラクターが実行される前に、すべてのインスタンス メンバーにデフォルト値 (false、0、null) があります。したがって、super()が呼び出されたときは、boolまだ false (ブール値のデフォルト値) です。

より一般的には、(ClassOne の) コンストラクターからオーバーライド可能なメソッドを呼び出すことは、今発見した理由から悪い考えです: まだ完全に初期化されていないオブジェクトで作業することになるかもしれません。

于 2013-07-15T15:29:32.580 に答える
0

super()この場合、すべてのコンストラクターで暗黙的に呼び出されるため、実際には不要です (しゃれは意図されていません)。これが意味することは、 のコンストラクターがClassOne最初に呼び出されるということです。そのため、コンストラクターが実行される前に、インスタンス メンバーには既定値があります (そうboolですfalse)。フィールドが初期化されるのは、コンストラクターが実行されただけです。

したがって、コンストラクターは効果的に次のようになります。

public ClassTwo() {
    super(); //call constructor of super class

    bool = true; //initialize members;
}

しかし、その時点でのClassOneの値を出力するオーバーライド可能なメソッドを呼び出します。boolfalse

一般に、( で行っているように) コンストラクターからオーバーライド可能なメソッドを呼び出すのは悪い習慣ClassOneです。これは、完全に初期化されていないオブジェクトを操作しているためです。

効果的な Java (第 2 版) から:

継承を許可するためにクラスが従わなければならない制限がいくつかあります。コンストラクターは、オーバーライド可能なメソッドを直接または間接的に呼び出してはなりません。この規則に違反すると、プログラムが失敗します。スーパークラス コンストラクターはサブクラス コンストラクターの前に実行されるため、サブクラスのオーバーライド メソッドは、サブクラス コンストラクターが実行される前に呼び出されます。オーバーライドするメソッドが、サブクラス コンストラクターによって実行される初期化に依存している場合、メソッドは期待どおりに動作しません。

于 2013-07-15T15:31:25.093 に答える
0

最終的な答えは次のとおりです。コンストラクターでオーバーライド可能なメソッドを使用しないでください。

すべてのコンストラクターで:

  • スーパーコンストラクターを呼び出します (コンストラクターで暗黙的または最初に)
  • 現在のクラスのフィールドの初期化を行います ( type field = value;)
  • 残りのコンストラクタを実行します

これが人生を面白くする

public class A {

    public A() {
        init();
    }

    protected void init() {
    }
}

public class B extends A {
    int a = 13;
    int b;

    @Override
    protected void init() {
        System.out.println("B.init a=" + a + ", b=" + b);
        a = 7;
        b = 15;
    }

    public static void main(String[] args) {
        new B().f();
    }

    public void f() {
        System.out.println("B.f a=" + a + ", b=" + b);
    }
}

これにより、

B.init a=0, b=0
B.f a=13, b=15
  1. B.init の呼び出し中と同様に、B フィールドはゼロになります。
  2. a7 (何もない) とb15に設定されます。
  3. 次に、B のコンストラクターaが初期化されます。

私の IDE は、コンストラクターでオーバーライド可能なメソッドを呼び出していることを不適切なスタイルとして既にフラグ付けしています。

于 2013-07-15T15:44:17.103 に答える