Какова возможная выгода (если таковая имеется) от разрешения рекурсивных конструкторов? - PullRequest
2 голосов
/ 07 апреля 2010

В Java конструкторы не могут быть рекурсивными. Ошибка времени компиляции: «вызов рекурсивного конструктора». Давайте предположим, что у нас не было этого ограничения.

Что нужно иметь в виду:

  • Тип возврата конструктора void. Поскольку это пустой метод, вы не можете использовать всю мощь рекурсии.
  • Конструктор может вызывать себя (или любой другой конструктор), используя this (). Но «вызов этого должен быть первым оператором в конструкторе»
  • Мы могли бы использовать нелокальные данные между последовательными вызовами, чтобы все еще иметь некоторую возможную выгоду от рекурсивных конструкторов.

Будет ли какая-то польза от использования рекурсивных конструкторов?

Ответы [ 6 ]

9 голосов
/ 07 апреля 2010

Конструкторы (когда они вызывают друг друга) похожи на методы, которые возвращают void. Следовательно, единственный способ, которым они могут дать результаты, - это побочные эффекты. Затем это ограничивается изменением объекта, который они создают, или изменением значений, передаваемых в качестве параметров. Последнее - довольно неприятная идея в конструкторе; конструктор обычно получает информацию из своих параметров, не изменяя их.

Таким образом, мутирование строящегося объекта - единственная возможность, чтобы иметь какой-либо способ отследить ход рекурсии, чтобы он в конце концов завершился. И очень трудно понять, как это было бы легче написать, яснее прочитать и т. Д., Чем простой цикл внутри обычного конструктора.

Вызов другого конструктора (с this) из конструктора, конечно, полностью отличается от использования выражения new внутри конструктора:

class Node
{
    Node _left, _right;

    public Node(Node left, Node right)
    {
        _left = left != null ? new Node(left._left, left._right) : null;
        _right = right != null ? new Node(right._left, right._right) : null;
    }
}

Здесь конструктор Node вызывает себя, но через новое выражение . Это принципиальная разница. Выражение new создает значение, так что это чисто «функциональный», не мутирующий материал и удобный способ сделать глубокую копию дерева узлов.

1 голос
/ 08 апреля 2010

Давайте посмотрим на эту проблему. Прежде всего, что происходит, когда вы вызываете new MyClass("foo");? Ну, есть две вещи. Прежде всего, виртуальная машина выделит память, необходимую для хранения объекта типа MyClass. Затем конструктор вызывается. Задача конструктора - инициализировать только что выделенную память. Следовательно, конструктор вообще не имеет возвращаемого типа (даже void). Значение, возвращаемое оператором new, является ссылкой на выделенную память, поэтому конструктор также не может вернуть.

Тогда какая польза от рекурсивного вызова конструктора. Единственным преимуществом такого вызова будет обработка некоторых параметров конструктора, таких как другие, и выполнение этого путем повторного вызова конструктора. Хотя это возможно, как правило, просто настроить значения в самом конструкторе (используя не конечные параметры) и после этого инициализировать атрибуты объекта (короче говоря, для этого вам не нужна рекурсия).

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

Более интересным вопросом является ограничение на super, или этот вызов является первым оператором конструктора. Это ограничение, вероятно, было введено, чтобы препятствовать небрежной или небезопасной практике программирования. Утверждение здесь выделено жирным шрифтом, хотя можно (хотя и не красиво) обойти это ограничение. Если вы помните, что выражения могут иметь побочные эффекты (например, присваивание переменных), а выражения, используемые для параметров, вызываются до самого вызова, можно создать сложные выражения, которые выполняют все ваши вычисления, перед вызовом конструктора делегата.

Основная причина, по которой вы хотите, чтобы позже вызывался делегат / супер-конструктор в теле конструктора, - это манипулирование параметрами. Вы можете сделать это с помощью (статических) вспомогательных функций, которые выполняют эти вычисления и предоставляют правильные значения. Это обычно чище, но не во всех случаях. На фактическую скорость выполнения не следует влиять, так как точка доступа может очень хорошо влиять на эти вещи.

Это означает, что в итоге рассмотрение сводится к предоставлению гибкости бесплатного размещения делегатских / супер-вызовов по сравнению с дополнительной безопасностью, обеспечиваемой тем, что неправильные практики становятся намного сложнее. Выбор, сделанный дизайнерами Java (и общей философией Java), состоит в том, чтобы усложнить выполнение неправильных действий за счет необработанных языковых возможностей со стороны экспертов (с повышенной сложностью). Сделанный выбор является для меня правильным, хотя лично мне хотелось бы иметь такую ​​мощь (всегда можно реализовать язык Java Java на JVM, который не имеет этих ограничений).

1 голос
/ 07 апреля 2010

Конструкторы могут быть рекурсивными . (Это в C #, но вы можете сделать то же самое в Java)

0 голосов
/ 08 апреля 2010

Возвращаемый тип конструктора недействительный.

Нет, это не так.

Конструктор может вызывать себя (или любой другой конструктор), используя this ()

Нет. Он может вызывать только другие конструкторы, и только если это не приведет к рекурсивному вызову текущего конструктора. Вот почему вы получаете сообщение об ошибке, на которое ссылались.

Мы могли бы использовать нелокальные данные между последовательные звонки, чтобы еще иметь некоторые возможный выигрыш от рекурсивного Конструкторы.

Как? Почему вы хотите re -инициализировать объект? Когда вы не можете сделать это последовательно за один проход? Никогда не было этой проблемы за 39 лет компьютерного программирования и 20 лет ОО.

Будет ли какая-то польза от разрешить рекурсивные конструкторы?

Вы еще не придумали ...

0 голосов
/ 07 апреля 2010

Что вы имеете в виду разрешить? Вы можете иметь рекурсивные конструкторы в Java. Они позволяют вам повторно использовать код и конструировать ваши конструкторы в более иерархической манере.

В следующем примере рекурсивного конструктора я могу вызвать new User() или new User("Marcus"), и с любым конструктором, который я использую, newUser имеет значение true.

public class User() {
  public String userName;
  public boolean newUser;
  User() {
    newUser = true;
  }
  User(String userName) {
    // Recursively call no-argument constructor
    this();
    this.userName = userName;
  }
}

Вот то же самое без рекурсивных конструкторов. Обратите внимание на двойную строку кода:

public class User() {
  public String userName;
  public boolean newUser;
  User() {
    newUser = true;
  }
  User(String userName) {
    newUser = true;
    this.userName = userName;
  }
}

В следующем нерекурсивном примере конструктора, если я не передам имя в конструктор, тогда имя будет установлено как «Новый пользователь». Я бы только хотел вызвать конструктор без аргументов, если я не устанавливаю имя. Если бы я сделал рекурсивный вызов конструктора здесь, я бы в конечном итоге установил имя пользователя дважды:

public class User() {
  public String userName;
  User() {
    this.userName = "New User";
  }
  User(String userName) {
    this.userName = userName;
  }
}

Вы будете использовать рекурсивные конструкторы, только если вы:

  1. иметь более одного конструктора
  2. Имейте код в своих конструкторах
  3. Хотите рекурсивно использовать код в другом конструкторе
0 голосов
/ 07 апреля 2010

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

...