7

私はこの状態にあります

public class A {
     public action() {
         System.out.println("Action done in A");
     }
}


public class B extends A {
     public action() {
         System.out.println("Action done in B");
     }
}

Bのインスタンスを作成すると、スーパークラスのアクションをオーバーライドするため、アクションはBのアクションのみを実行します。

問題は、私のプロジェクトでは、スーパークラスAがすでに何度も使用されていることです。特定の条件下で、Aのインスタンスを作成するときにチェックを行い、それがtrueの場合はそれ自体を置き換える方法を探しています。 Bで。

public class A {
     public A() {
         if ([condition]) {
            this = new B();
         }
     }

     public action() {
         System.out.println("Action done in A");
     }
}

A a = new A();
a.action();
// expect to see "Action done in B"...

これは何らかの方法で可能ですか?

4

5 に答える 5

6

私はこれを行うと言うでしょう:

this = new B();

のコンストラクター内ではA、たとえそれが可能であったとしても、オブジェクト指向の設計原則に違反します (実際にはそうではありません)。

そうは言っても、このシナリオに直面した場合:

問題は、私のプロジェクトでは、スーパークラス A がすでに何度も使用されていることです

私は次の 2 つの方法のいずれかで解決します:
あなたの条件は、タイプ A のオブジェクトをあまり多くしたくないということであると仮定しました。それ以外の場合は、他の条件で自由に置き換えてください。

オプション 1 : 工場で設計されたパターンを使用します。

public class AFactory
{
    private static count = 0;
    private static final MAX_COUNT = 100;

    public A newObject() {
        if (count < MAX_COUNT) {
            count++;
            return new A();
        } else {
            return new B();
        }
    }
}

そして、次のようにオブジェクトを生成します。

A obj1 = factory.newObject();
A obj2 = factory.newObject();

オプション 2 : 静的カウンター + try&catch

コンストラクターで静的変数を 1 ずつインクリメントすることにより、A がインスタンス化された回数を追跡する A クラス内で静的カウンターを使用します。タイプ A のオブジェクトの最大数の制限に達した場合はInstantiationError、A のコンストラクターで an をスローします。

これは、A をインスタンス化するたびに、try..catchブロックで をインターセプトし、代わりInstantionErrorに型の新しいオブジェクトを作成する必要があることを意味します。B

public class A {

    private static count = 0;
    private static final MAX_COUNT = 100;

    public A() {
        if (count > 100) {
            throw new InstationError();
        }
    }

}

そして、オブジェクトを生成するとき:

A obj1, obj2;
try {
    obj1 = new A();
} catch (InstantiationError ie) {
    obj1 = new B();
}
try {
    obj2 = new A();
} catch (InstantiationError ie) {
    obj2 = new B();
}

オプション 2 は、質問で直接尋ねる内容に最も近いものです。ただし、個人的にはファクトリ デザイン パターンを使用することを選択します。それは、はるかにエレガントなソリューションであり、とにかくやりたいことを達成できるからです。

于 2010-05-05T12:53:29.273 に答える
5

直接ではありません。を呼び出すnew A()と、常に のインスタンスが作成されますAAただし、コンストラクターを保護してから、静的メソッドを作成することもできます。

public static A newInstance() {
  // Either create A or B here.
}

次に、コンストラクタへの現在のすべての呼び出しをファクトリ メソッドへの呼び出しに変換します。

于 2010-05-05T12:52:40.233 に答える
4

スーパークラスのコンストラクタを使用するかどうかを選択できますか?

スーパークラスのコンストラクターの 1 つを独自のオブジェクトを構築する前に呼び出す必要があるため、スーパークラスのコンストラクターを使用するかどうかを条件付きで制御することはできません。

上記から、Java には、コンストラクターの最初の行でスーパークラスのコンストラクターを呼び出す必要があるという要件があります。実際、スーパークラスのコンストラクターへの明示的な呼び出しがなくても、暗黙的な呼び出しが行われます。にsuper():

public class X {
   public X() {
      // ...
   }

   public X(int i) {
      // ...
   }
}

public class Y extends X {
   public Y() {
     // Even if not written, there is actually a call to super() here.
     // ...
   }
}

他の何かを実行した後にスーパークラスのコンストラクターを呼び出すことはできないことに注意してください。

public class Y extends X {
   public Y() {
     doSomething();  // Not allowed! A compiler error will occur.
     super();        // This *must* be the first line in this constructor.
   }
}

代替手段

つまり、ここで必要なことを達成する方法は、何らかの条件に応じて実装の種類を選択できるファクトリ メソッド patternを使用することです。

public A getInstance() {
  if (condition) {
     return new B();
  } else {
     return new C();
  }
}

上記のコードでは、 に応じて、メソッドはまたはconditionのインスタンスを返すことができます(両方が class のサブクラスであると仮定します)。BCA

interface以下は、ではなくを使用した具体的な例classです。

次のインターフェイスとクラスがあるとします。

interface ActionPerformable {
  public void action();
}

class ActionPerformerA implements ActionPerformable {
  public void action() {
    // do something...
  }
}

class ActionPerformerB implements ActionPerformable {
  public void action() {
    // do something else...
  }
}

次に、メソッドを介して渡される条件に応じて、上記のクラスのいずれかを返すクラスがあります。

class ActionPeformerFactory {

  // Returns a class which implements the ActionPerformable interface.
  public ActionPeformable getInstance(boolean condition) {

    if (condition) {
      return new ActionPerformerA();
    } else {
      return new ActionPerformerB();
    }
  }
}

次に、条件に応じて適切な実装を返す上記のファクトリ メソッドを使用するクラス:

class Main {
  public static void main(String[] args) {

    // Factory implementation will return ActionPerformerA
    ActionPerformable ap = ActionPerformerFactory.getInstance(true);

    // Invokes the action() method of ActionPerformable obtained from Factory.
    ap.action();    
  }
}
于 2010-05-05T12:57:35.527 に答える
2

これは、ファクトリ メソッドを使用した場合に可能です。コンストラクターを使用する場合: いいえ、完全に不可能です。

于 2010-05-05T12:52:56.107 に答える
1

インターフェイスやファクトリに移動する気がないと仮定すると、B のデリゲート コピーを A に保持し、メソッドを書き直してデリゲートを呼び出すことができます。

public class A{
  B delegate;

  public A(){
    if([condition]){
      delegate = new B()
      return;
    }
    ...//normal init
  }

  public void foo(){
    if(delegate != null){
      delegate.foo();
      return;
    }
    ...//normal A.foo()
  }

  public boolean bar(Object wuzzle){
    if(delegate != null){
      return delegate.bar(wuzzle);
    }
    ...//normal A.bar()
  }
  ...//other methods in A
}
于 2010-05-05T12:56:51.423 に答える