Подтвердить что ты не робот

Перейти к родительскому конструктору, чтобы вызвать дедушку

Проблема заключается в следующем: у меня есть абстрактный класс, который выполняет некоторую работу в его конструкторе и набор дочерних классов, реализующих абстрактный класс:

class AbstractClass {
     AbstractClass(){ /* useful implementation */ }
}

class ConcreteClass1 extends AbstractClass {
     ConcreteClass1(){ super(); /* useful implementation */ }
}

Затем конкретные классы должны быть настроены, а одно решение - расширить конкретные классы:

class CustomizedClass1 extends ConcreteClass1 {
    CustomizedCLass1(){ super(); /* useful implementation */ }
}

НО проблема заключается в том, что настраиваемые классы нужно только вызвать конструктор абстрактного класса, а не конкретный конструктор класса.

Как вы это достигаете? Предложения по изменению отношений классов действительны.

EDIT: конкретный пример: ConcreteClass1 и CustomizedClass1 имеют разные наборы данных (ConcreteData1 и CustomizedData1) и извлекаются из базы данных в конструкторе класса. Проблема в том, что создание экземпляра CustomizedClass1 будет извлекать обе части данных.

Я знаю, что, используя простое наследование, это, вероятно, не самое лучшее, что нужно сделать, поэтому я указал, что предложения по изменению отношений классов действительны.

4b9b3361

Ответ 1

Легко (но почему?):

class AbstractClass {
   AbstractClass(){ /* useful implementation */ }
}

class ConcreteClass1 extends AbstractClass {
     ConcreteClass1(){ super(); /* useful implementation */ }
     ConcreteClass1(boolean skip){ super(); }
}
class CustomizedClass1 extends ConcreteClass1 {
     CustomizedCLass1(){ super(true); /* useful implementation */ }
}

Ответ 2

Вы не можете сделать это на Java. У меня часто есть ученики, которые хотят это сделать, но я никогда не видел случая, когда это действительно то, что они хотели сделать.

Можете ли вы привести конкретный пример того, что вы хотите сделать и почему (ваше описание слишком неопределенно), и я уверен, что решение может быть достигнуто:-)

Edit:

Для реального мира пример того, почему вы не хотите этого делать (обычно), будет такой иерархией, как:

Animal (constructor makes the eyes)
  |
Mammal (constructor makes the lungs)
  |
Human (constructor sets the language)

Если конструктор Человека может пропустить конструктор Млекопитающего, тогда вы закончите с Человеком, у которого нет легких... не очень полезно.

Ответ 3

Любой экземпляр CustomizedClass1 также является экземпляром ConcreteClass1, по определению, поэтому он должен быть сконструирован как действительный экземпляр ConcreteClass1 перед тем, как конструктор CustomizedClass1 может запускаться. Иначе что произойдет, если вы назовете на нем методы ConcreteClass1? Они будут пытаться работать с переменными, которые еще не были инициализированы.

Если вы думаете, что вам нужно это сделать, скорее всего, ваш дизайн нуждается в переосмыслении. Например, если вы хотите использовать только некоторые функции из ConcreteClass1, эта функциональность может быть включена в суперкласс из ConcreteClass1, а CustomizedClass1 может расширить это, чтобы получить только те функции, которые ему нужны.

Просьба предоставить дополнительную информацию о взаимосвязи между этими классами.

Ответ 4

Два комментария: Во-первых, вы не должны мыслить в терминах "прыгающих" конструкторов, подобных этому. Во-вторых, это действительно звучит так, будто вам нужно переосмыслить свои отношения в классе.

В любое время, когда вы думаете, что "A extends B, кроме этого...", - очень хорошее время, чтобы смотреть на вещи дальше. "Расширения" подразумевает, что "есть", который является либо/или отношением: с необязательным поведением добавляются серые области, которые позже вас укусят.

Как говорили люди, вы можете предоставить несколько конструкторов ConcreteClass1 для выполнения требуемой инициализации в каждом случае, возможно, для их защиты, чтобы они могли использоваться только подклассами. Но вот вопрос: что, если кто-то хочет написать CustomizedClass2, которому нужны некоторые (но не все) функции в ConcreteClass1? Добавляете ли вы еще один пользовательский конструктор?

Ответ 5

Это звучит для меня как смесь проблем - что-то Java не очень хорошо разбирается.

Хотя это не тот ответ, на который вы надеялись, или тот, которым я горжусь, вы можете просто создать ConcreteClass2, который имитирует ConcreteClass1 и использует конструктор AbstractClass.

Как сказал @TofuBeer, это не то, что поддерживает Java. Вот почему некоторые современные языки (т.е. Scala w/Traits) набирают страстных разработчиков.

Ответ 6

Является ли CustomizedData1 подклассом ConcreteData1? Если это так, то я бы предложил иметь (возможно, защищенный) конструктор для ConcreteClass1, который использует ConcreteData1 для использования вместо того, чтобы извлекать его во время инициализации. Таким образом, CustomizedClass1 может получить свой CustomizedData1 и передать его на вызов super. К сожалению, это может быть сложно или довольно невозможно, если вы не можете получить данные до некоторого внутреннего init.


class ConcreteClass1 extends AbstractClass {
     ConcreteClass1(){
          this(...fetch data as before...);
     }
     ConcreteClass1(ConcreteData1 data){
          myData = data;
          ...
     }
}
class CustomizedClass1 extends ConcreteClass1 {
     CustomizedCLass1(){
          super(new CustomizedData1(...));
          ... 
     }
}

Но тогда CustomizedClass1, вероятно, нуждается в ссылке на данные как CustomizedData1, а не только на ConcreteData1. Это может просто привести в порядок ее унаследованную ConcreteData1 все время, но это кажется yucky. Но если он сохраняет свою собственную ссылку на данные, тогда необходимо синхронизировать ссылки, если они не являются окончательными.

Ответ 7

Почему бы просто не настроить только что созданный экземпляр ConcreteClass1, чтобы вести себя как экземпляр AbstractClass (при условии, что ConcreteClass1 имеет соответствующие защищенные методы только для этого)? То есть:.

class AbstractClass {
     public AbstractClass() { /* useful implementation */ }
}

class ConcreteClass1 extends AbstractClass {
     public ConcreteClass1() { 
        /* A hidden super() call put here by the compiler. */
        /* Useful implementation */ 
     }

     protected void customize_with(SomeParams customization_params) {
        /* 
        Customize this ConcreteClass1 instance according to parameters 
        passed. As a result, the instance behavior will 'revert' (in the 
        way you need it) to that of an AbstractClass instance.
        */
     }
}

class CustomizedClass1 extends ConcreteClass1 {
    public CustomizedCLass1() {
        /* A hidden super() call put here by the compiler. */
        customize_with(customization_params);
        /* Rest of useful implementation */ 
     }
}

Замыслы и логика дизайна здесь могут быть следующими:

  • Вы хотите получить (базовое) поведение ConcreteClass1 через наследование, вы наследуете его (это, конечно, налагает на то, чтобы оно было наследуемым).

  • По умолчанию вы настроите поведение, предоставляемое ConcreteClass1. Настройка, которую вы хотели бы получить, обычно может быть описана с помощью некоторых параметров. Просто передайте эти параметры специальному методу CustomizedClass1 (который можно защитить) и назовите его customize() соответственно.

  • Настройка, выполняемая в конструкторе ConcreteClass1(), может быть любым, в частности, поведение экземпляра класса может быть "возвращено" к типу AbstractClass, о котором вы, вероятно, просите (если я правильно понял).

  • Вызов customize_with() может фактически ввести некоторые накладные расходы в зависимости от фактического материала, сделанного в ConcreteClass1(), в этом случае использование перегруженных (и, возможно, защищенных) конструкторов, безусловно, является лучшим решением, если вы не хотите ConcreteClass1 "экземпляры должны быть динамически настраиваемыми (в этом случае customize_with() и, возможно, остальная часть ConcreteClass1 должна быть разработана соответственно, т.е. поддерживать такое поведение по контракту).

Извините, если что-то не так с синтаксисом (я не написал много Java-кода).

Ответ 8

Один из способов я придумал:

class AbstractClass {
     AbstractClass(){ init(); }
     protected init(){ /* useful implementation */ }
}

class ConcreteClass1 extends AbstractClass {
     ConcreteClass1(){ init(); /* useful implementation */ }
}

class CustomizedClass1 extends ConcreteClass1 {
    CustomizedCLass1(){ init(); /* useful implementation */ }
}

Таким образом, CustomizedClass1 получает нужное поведение из AbstractClass, не проходя через инициализацию ConcreteClass1.

EDIT: Это не работает, потому что родительские конструкторы неявно называются одним из комментаторов, я думаю, что простой способ состоит в том, чтобы иметь разные конструкторы.

Ответ 9

Да, вы можете настроить! Добавьте другой метод в родительский класс (B), а в вашем классе Child (C) назовите его super.executeParentA(); в этом методе вызов super.execute()

A --> execute() 

B --> executeParent_A() { super.execute(); }

C --> super.executeParent_A();

-Mehboob

Ответ 10

Я могу думать о двух случаях, когда можно было бы это сделать (на самом деле нет):

Случай 1, ConcreteClass2 запускает общую инициализацию из верхнего класса, но затем делает свою собственную последовательность инициализации, которая отличается/конфликтует, чем таковая в ConcreteClass1 → , имеет метод init() и переопределяет ее (вместо того, чтобы пытаться переопределить конструктор ConcreteClass1).

Случай 2, у вас есть полиморфная инициализация одного (или более) члена класса (это на самом деле конкретный случай предыдущего):

public class ConcreteClass1 extend AbstractClass
{
  protected F1 f;

  public ConcreteClass1 () {
    super ();
    this.f = new F1();
  }
}

public ConcreteClass2 extends ConcreteClass1
{
  public ConcreteClass2 () {
    super (); // I'd like to do super.super() instead (no, you don't)
    this.f = new F2(); // We wasted time with new F1 ();
  }
}

В этом случае используйте либо метод init(), либо выполните следующее:

protected ConcreteClass1 ( F1 f ) {
  super ();
  this.f = f;
}
public ConcreteClass1 () {
  this ( new F1 () );
}

...

public ConcreteClass2 () {
  super ( new F2 () );
}

Другими словами, сделать причину явного перехода, поскольку неявное переключение по иерархии запрещено и по уважительным причинам объяснено в других ответах.