Java:Superclass在某些条件下构造子类,可能吗?

时间:2010-05-05 12:44:58

标签: java subclass superclass

我有这个条件

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的实例时,它会进行检查并且是否为真的方式,用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"...

这在某种程度上是可能的吗?

5 个答案:

答案 0 :(得分:6)

我会说这样做:

this = new B();
A的构造函数中的

会违反OO设计原则,即使有可能(但事实并非如此)。

话虽如此,如果我遇到这种情况:

  

问题是在我的项目中,超类A已被使用了太多次

我会用以下两种方式之一来解决它:
我假设您的条件是您不需要太多类型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&amp; catch

在A类中使用静态计数器,通过在构造函数中将静态变量增加1来跟踪A实例化的次数。如果它达到A类对象最大数量的限制,则在A的构造函数中抛出InstantiationError

这意味着无论何时实例化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最接近您在问题中直接提出的要求。但是,我个人会选择使用工厂设计模式,因为它是更优雅的解决方案,它可以让你实现你想要做的任何事情。

答案 1 :(得分:5)

不直接,不。调用new A()将始终创建A的实例。但是,您可以使A的构造函数受到保护,然后使用静态方法:

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

然后将所有当前调用转换为构造函数以调用工厂方法。

答案 2 :(得分:4)

可以选择是否使用超类的构造函数?

不可能有条件地控制是否使用超类的构造函数,因为在构造自己的对象之前必须调用其中一个超类构造函数。

从上面可以看出,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.
   }
}

替代

也就是说,实现此处所需的方法可能是使用factory method pattern,它可以根据某种条件选择实现类型:

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

在上面的代码中,根据condition,该方法可以返回BC的实例(假设它们都是类A的子类)。

示例

以下是使用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();    
  }
}

答案 3 :(得分:2)

如果你使用factory method,这是可能的。当你使用构造函数时:nope,完全不可能。

答案 4 :(得分:1)

假设你不愿意将它移动到界面或工厂那么丑陋但是你可以在A中保留B的删除副本并重写你的方法来调用委托:

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
}