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 的实例时,它会进行检查,如果是真的,替换自己与 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();

在构造函数中 forA会违反 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&catch

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

这意味着每当您实例化 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()将始终创建A. 但是,您可以制作A受保护的构造函数,然后使用静态方法:

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

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

于 2010-05-05T12:52:40.233 回答
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.
   }
}

替代方案

也就是说,实现此处所需的一种方法可能是使用工厂方法模式,它可以根据某种条件选择实现类型:

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

在上面的代码中,取决于,该方法可以返回或condition的实例(假设两者都是 class 的子类)。BCA

例子

以下是一个具体示例,使用的是 ainterface而不是 a 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

假设您不愿意将其移至接口或工厂,但您可以在 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
}
于 2010-05-05T12:56:51.423 回答