0

私はこの継承構造を持っています:

public abstract class Mom {
    int dummy;
    Mom() {
        dummy = 0;
    }
    Mom(int d) {
        this();
        dummy = d;
    }
}
public class Kid extends Mom {
    String foo;
    Kid() {
        super();
        foo = "";
    }
    Kid(int d) {
        super(d);
    }
}
// ...
Kid kiddo = new Kid(10);
// kiddo.foo == null !

私の引数なしのコンストラクターKidは呼び出されません! これが私が期待したものです:

  1. new Kid(10)Kid#Kid(int)
  2. super(d)Mom#Mom(int)
  3. this()Kid#Kid()// どっ!
  4. super()Mom#Mom()

からの引数なしのコンストラクターMomを呼び出すことは可能ですか? そうではないと思います。呼び出す抽象メソッド [1]を追加し、 sをオーバーライドする必要があります。 しかし、私は正確な理由を知りたかっただけであり、可能であれば、サブクラスのコンストラクターを呼び出すことが悪い考えである理由を証明する例 (サブクラスのコンストラクター.Kid
init()MomKid
super()

// in Mom:
protected abstract void init();
public Mom() {
    dummy = 0;
    init();
}

// in Kid:
@Override
protected abstract void init() {
    foo = "";
}
4

3 に答える 3

6

すべてのコンストラクターを呼び出す必要がないように、これらを配置する方法。

public abstract class Parent {
    final int dummy;

    Parent () {
        this(0);
    }
    Parent (int d) {
        dummy = d;
    }
}

public class Kid extends Parent {
    final String foo = "";

    Kid() {
    }

    Kid(int d) {
        super(d);
    }
}

を使用finalすると、すべてのフィールドが 1 回設定されます。


コンストラクターからオーバーライド可能なメソッドを呼び出すことは悪い習慣と考えられているため、コンストラクターをオーバーライド可能にすることは悪い考えです。


this()コンストラクターは継承に従わない (静的メソッドも従わない) ため、同じクラスのコンストラクターを呼び出します。

new Kid(10) --> Kid#Kid(int)
super(d) --> Mom#Mom(int)
this() --> Mom#Mom()

そうしないと、同じコンストラクターを複数回呼び出す危険があり、最終メソッドが 1 回だけ設定されることを保証する方法がありません。

于 2012-08-09T08:58:37.657 に答える
2

JLS §8.8.7.1から(私が強調):

  • 代替コンストラクターの呼び出しは、キーワードで始まりますthis(明示的な型引数で始まる可能性があります)。これらは、同じクラスの代替コンストラクターを呼び出すために使用されます。

  • スーパークラス コンストラクターの呼び出しは、キーワードsuper(明示的な型引数で始まる可能性があります) または Primary 式のいずれかで始まります。これらは、直接のスーパークラスのコンストラクターを呼び出すために使用されます。

そのため、this-constructor-invocation は常に同じクラスを参照し、子クラスを参照することはありません。

コンストラクターで仮想メソッドを呼び出すことは可能ですが、部分的に初期化されたオブジェクト インスタンスでこれらのメソッドが動作する可能性があるため、安全ではなく、悪い習慣と見なされます。

あなたの問題には、いくつかの可能な解決策があります:

  1. 宣言でメンバーfoofoo = "";を初期化します。これは、フィールド初期化子とも呼ばれます
  2. インスタンス初期化子を使用します: { foo = ""; }. クラスで必要に応じて、複数のインスタンス初期化子を使用できることに注意してください。
  3. 弾丸を噛み、すべてのコンストラクターで初期化を繰り返します

JLS §12.5によると、(1) と (2) の初期化は常にコンストラクター自体が呼び出される前に実行されるため、問題のあるパターンに頼る必要なく、明確に定義されたオブジェクトの初期化を行うことができます。

メンバーが複数回初期化された場合、最後の初期化が優先されます。

4) このクラスのインスタンス初期化子とインスタンス変数初期化子を実行し、インスタンス変数初期化子の値を対応するインスタンス変数に割り当てます。この順序は、クラスのソース コードにテキストで表示される左から右の順序です。

同じフィールドがコンストラクターでも初期化されている場合は、コンストラクターが優先されます。

于 2012-08-09T09:21:57.353 に答える
0

Kid クラスには次のコンストラクタが必要です。

Kid(int i) {
  super(i);
  whatever();
}

Kid () {
  this( DEFAULT_VALUE);
}

親コンストラクターへのすべての呼び出しが、子クラスの完全修飾コンストラクターを介して行われるようにします。また、現在のコードの場合と同様に、バイパスされないクラスのすべてのコンストラクターのデフォルトの動作を設定します。

于 2012-08-09T09:01:25.787 に答える