2010-04-07 5 views
2

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

Вещи, чтобы иметь в виду:

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

Может ли быть какая-либо польза от разрешения рекурсивных конструкторов?

+0

Несомненно, конструктор - это метод, который возвращает новый экземпляр создаваемого класса, а не null? – Finbarr

+0

@ Finbarr Нет, это был бы заводский метод; сам конструктор не имеет возвращаемого значения. –

+0

@Finbarr - никто не упомянул 'null'! Конструкторы, строго говоря, не то же самое, что и другие методы, поэтому нет смысла точно описывать их как имеющие тип возврата вообще. Тело конструктора ничего не возвращает, и когда один конструктор вызывает другого, промежуточное возвращаемое значение отсутствует, поэтому ближайшая аналогия имеет метод, который возвращает 'void'. –

ответ

9

Конструкторы (когда они звонят друг другу) похожи на методы, возвращающие 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 конструктор называет себя, но через новое выражение , Это ключевое различие. A new выражение создает значение, поэтому это чисто «функциональный», не мутирующий материал и обеспечивает удобный способ сделать глубокую копию дерева узлов.

0

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

+0

Возможно, вы это сделали, не думая об этом как о рекурсии - например, глубокий клон объекта, переданного в конструктор. –

1

Constructors can be recursive. (Это в C#, но вы можете сделать то же самое в Java)

+1

Construct * ion * может быть рекурсивным, если вы включаете построение других объектов в ходе строительства, но это обман! :) –

+0

Однако этот пример также показывает реальный прецедент. – SLaks

+0

Он делает это, но вызывает рекурсивно через выражение 'new', а не прямой вызов конструктора через' this' - да, вы тоже это делаете, но это не то, что делает ваш пример рекурсивным. Я уточнил свой ответ, чтобы прояснить эту разницу. –

0

Что значит разрешить? Вы можете иметь рекурсивные конструкторы в 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. Хотите рекурсивно использовать код, который находится в другом конструкторе
+1

Это не рекурсия, это повторное использование; методы с разными аргументами - это разные методы (перегрузка), поэтому вы не вызываете один и тот же конструктор. Вы вызываете другой конструктор. Речь идет о вызове того же конструктора (и потому каким-то образом динамично решаем, делать ли это, чтобы в конечном итоге прекратить действие), что является незаконным. –

+0

@ Даниэль, я могу поклясться, из «вещей, которые нужно иметь в виду», вот в чем вопрос, но мы увидим, когда ответит человек. –

+0

Я имел в виду рекурсию. Я полностью осведомлен о повторном использовании конструкторов, чтобы использовать менее специфичные для вызова самого (или более) конкретного конструктора. – Penang

0

The return type of a constructor is void.

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

A constructor can invoke itself (or any other constructor) using this()

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

We could use non local data between consecutive calls to still have some possible gain from recursive constructors.

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

Would there be any benefit from allowing recursive constructors?

Вы не придумали какой-то ...

1

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

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

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

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

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^