У меня есть это условие
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 уже используется слишком много раз, и я ищу путь, который при определенных условиях, когда я создаю экземпляр этого, осуществляет проверку и если это верно, замените себя 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"...
действительно ли это возможно в некотором роде?
Я бы сказал, что выполнение этого:
this = new B();
в конструкторе для A
нарушит принципы объектно-ориентированного проектирования, даже если это возможно делать (а это не так).
При этом, если бы я столкнулся с таким сценарием:
проблема в том, что в моем проекте суперкласс A уже используется слишком много раз
, я бы решил ее одним из двух следующих способов :
Я предположил, что ваше условие состоит в том, что вам не нужно слишком много объектов типа A, в противном случае вы можете заменить их любым другим условием.
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();
Используйте статический счетчик в вашем классе 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 наиболее близок к тому, что вы задаете прямо в вопросе. Однако я лично предпочел бы использовать шаблон проектирования фабрики, потому что это гораздо более элегантное решение, и оно позволяет вам в любом случае достичь того, чего вы хотите.
Это было бы возможно, если бы вы использовали фабричный метод . Когда вы используете конструктор: нет, совершенно невозможно.
Невозможно условно контролировать, использовать или нет конструктор суперкласса, поскольку один из конструкторов суперкласса должен быть вызван перед созданием собственного объекта.
Из вышесказанного следует, что в 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();
}
}
В приведенном выше коде, в зависимости от условия
, метод может вернуть либо экземпляр B
, либо C
(предполагая, что оба являются подклассом класса A
).
Пример
Ниже приведен конкретный пример, использующий интерфейс
, а не класс
.
Пусть существуют следующие интерфейсы и классы:
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();
}
}
Предполагая, что вы не хотите переносить его в интерфейс или фабрику, это выглядит некрасиво, но вы могли бы сохранить делегированную копию B в A и переписать ваши методы ot вызова делегата:
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
}
Не напрямую, нет. Вызов new A()
всегда создаст экземпляр A
. Однако можно сделать конструктор A
защищенным, а затем иметь статический метод:
public static A newInstance() {
// Either create A or B here.
}
Затем преобразовать все текущие вызовы конструктора в вызовы фабричного метода.