开发者

Java: Superclass to construct a subclass on certain conditions, possible?

I have this condition

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


public class B extends A {
     public action() {
         System.out.print开发者_如何学Goln("Action done in B");
     }
}

when I create an instance of B, the action will do just actions in B, as it overrides the action of the superclass.

the problem is that in my project, the super class A is already used too many times, and I am looking for a way that under certain conditions, when i create an instance of A it makes a check and if it is true, replace itself with 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"...

is this possible in some way?


I would say that doing this:

this = new B();

within the constructor for A would violate OO design principles, even it were possible to do (and it isn't).

That being said, if I were faced with this scenario:

the problem is that in my project, the super class A is already used too many times

I would solve it in one of the two following ways:
I have assumed that your condition is that you do not want too many objects of type A, otherwise feel free to substitute in any other condition.

Option 1 : Use a factory design pattern.

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();
        }
    }
}

And then somehwere else you generate the objects like so:

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

Option 2 : Static counter + try&catch

Use a static counter within your A class that keeps track of the number of times A has been instantiated, by incrementing the static variable by one in the constructor. If it hits a limit for the max number of object of type A, throw an InstantiationError in A's constructor.

This would mean that whenever you instantiate A, you have to a try..catch block to intercept the InstantionError, and then create a new object of type B instead.

public class A {

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

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

}

And when generating your objects:

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

Option 2 is closest to what you ask directly in the question. However, I would personally choose to use the factory design pattern, because it is much more elegant solution, and it allows you to achieve what you want to do anyway.


Not directly, no. Calling new A() will always create an instance of A. However, you could make the constructor of A protected, and then have a static method:

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

Then convert all current calls to the constructor to calls to the factory method.


It is possible to choose whether or not to use a superclass' constructor?

It is not possible to conditionally control whether or not to use a superclass' constructor, as one of the superclass' constructors must be called before constructing one's own object.

From the above, there is a requirement in Java, that the first line of the constructor must call on of the superclass' constructor -- in fact, even if there is no explicit call to a superclass' constructor, there will be an implicit call to 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.
     // ...
   }
}

It should be stressed that it is not possible to call the superclass' constructor after performing something else:

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.
   }
}

The alternative

That said, a way to achieve what is desired here could be to use the factory method pattern, which can select the kind of implementation depending on some kind of condition:

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

In the above code, depending on condition, the method can return either an instance of B or C (assuming both are a subclass of class A).

Example

The following is a concrete example, using an interface rather than a class.

Let there be the following interfaces and classes:

interface ActionPerformable {
  public void action();
}

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

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

Then, there would be a class which will return one of the above classes depending on a condition which is passed in through a method:

class ActionPeformerFactory {

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

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

Then, a class which uses the above factory method which returns the appropriate implementation depending on a condition:

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();    
  }
}


This would be possible if you used a factory method. When you use a constructor: nope, completely impossible.


Assuming you aren't willing to move it to an interface or factory its kind of ugly but you could keep a delegeate copy of B in A and rewrite your methods ot invoke the delegate:

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
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜