views:

267

answers:

6

In Java, constructors cannot be recursive. Compile time error: "recursive constructor invocation". Let's assume that we did not have this restriction.

Things to keep in mind:

  • The return type of a constructor is void. Since it is a void method you can't harness the complete power of recursion.
  • A constructor can invoke itself (or any other constructor) using this(). But a "call to this must be first statement in constructor"
  • We could use non local data between consecutive calls to still have some possible gain from recursive constructors.

Would there be any benefit from allowing recursive constructors?

+7  A: 

Constructors (when they are calling each other) are like methods that return void. Consequently the only way they can produce results is by side-effects. This is then limited to mutating the object they are constructing or by mutating the values passed in as parameters. The latter is a pretty nasty idea in a constructor; a constructor usually takes information from its parameters without mutating them.

So mutating the object being constructed is the only option in order to have any way to track the progress of the recursion, in order for it to terminate eventually. And it's very hard to see how that would be easier to write, clearer to read, etc. than a simple loop inside an ordinary constructor.

Calling another constructor (with this) from within a constructor is of course totally different from using a new expression within a constructor:

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;
    }
}

Here the Node constructor calls itself, but via a new expression. This is the crucial difference. A new expression produces a value, so this is purely "functional", non-mutating stuff, and provides a convenient way to make a deep copy of the tree of nodes.

Daniel Earwicker
+1  A: 

You might not be able to write a recursive constructor, but you can call a recursive function from your constructor. I have never had to do this before, and I can't think of a situation where you might need to, but you can do it if you want.

Mark Byers
You may have done it without thinking of it as recursion - a deep clone of an object passed into the constructor, for example.
Daniel Earwicker
+1  A: 

Constructors can be recursive. (That's in C#, but you can do the same thing in Java)

SLaks
Construct*ion* can be recursive, if you include constructing other object instances during construction, but that's cheating! :)
Daniel Earwicker
However, that example also shows a real use case.
SLaks
It does, but of calling recursively via a `new` expression, not a direct call to the constructor via `this` - yes, you do that too, but it isn't what makes your example recursive. I've updated my answer to clarify this difference.
Daniel Earwicker
I realize that. However, that answer shows what you can do.
SLaks
A: 

What do you mean allow? You can have recursive constructors in Java. They allow you to reuse code and design your constructors in a more hierarchical fashion.

In the following recursive constructor example, I can call new User() or new User("Marcus") and with either constructor that I use, newUser is set to 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;
  }
}

Here's the same thing without recursive constructors. Notice the duplicate line of code:

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

In the following non-recursive constructor example, if I don't pass a name into the constructor, then the name is set to "New User". I would only want to call the no-argument constructor if I'm not setting the name. If I did a recursive constructor call here, I would end up setting the userName twice:

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

You will only use recursive constructors if you:

  1. Have more than one constructor
  2. Have code in your constructors
  3. Want to recursively use code that's in another constructor
Marcus Adams
That's not recursion, it's reuse; methods with different arguments are different methods (overloading), so you are not calling the same constructor. You're calling a different constructor. The question is about calling the same constructor (and therefore somehow dynamically deciding whether to do so, in order to terminate eventually), which is illegal.
Daniel Earwicker
@Daniel, I could swear, from the "things to keep in mind", this is what the question was, but we'll see when the person responds.
Marcus Adams
I meant recursion. I'm completely aware of reusing constructors to use the less specific ones to invoke the most (or more) specific constructor.
Penang
The clue is in the fact that the question asks about the theoretical value of enabling something that is currently disallowed.
Daniel Earwicker
A: 

The return type of a constructor is void.

No it isn't.

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

No. It can only invoke other constructors, and only if that won't lead to a recursive invocation of the current constructor. That's why you get the error message you referred to.

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

How? Why would you want to re-initialize an object? When can't you do it sequentially in one pass? Never had this problem in 39 years of computer programming, and 20 years of OO.

Would there be any benefit from allowing recursive constructors?

You haven't come up with any ...

EJP
+1  A: 

Let's look at this problem. First of all, what happens when you invoke new MyClass("foo");? Well there are two things happening. First of all, the virtual machine will allocate the memory needed to store an object of type MyClass. Then, the constructor gets called. The job of a constructor is to initialise this just allocated memory. Therefore, a constructor does not have a return type at all (not even void). The value returned by the new operator is a reference to the allocated memory, so the constructor can not return as well.

Then, what would be the benefit of recursive constructor invocation. The only benefit of such invocation would be to handle certain constructor parameters like others, and doing so by re-invoking the constructor. While this is possible it is generally easy just to adjust the values in the constructor itself (using non-final parameters), and after that initialise the object attributes (in short you don't need recursion for this).

Second, you can do recursion fairly easily by offloading all the work to a worker method that can recurse as much as you want.

A more interesting question is the restriction on super or this invocation being the first statement of the constructor. This restriction was probably put in to discourage sloppy or unsafe programming practices. Statement is put in bold here though as it is possible (although not beautiful) to work around this restriction. If you remember that expressions may have side effects (e.g. variable assignments), and expressions used for parameters are invoked before the call itself it is possible to create complicated expressions that do all your calculations before invoking the delegate constructor.

The general reason why you want to have a delegate/super constructor invocation later in the constructor body is parameter manipulation. You can do that with (static) helper functions that do these calculations and provide the correct values. This is generally cleaner but not in all cases. The actual execution speed should not be affected as hotspot can inline these things very well.

That means that in the end the consideration boils down to providing the flexibility of free placement of delegate/super calls versus the added safety provided by making incorrect practices quite much harder. The choice made by Java's designers (and the general Java philosophy) is to go for making it harder to do the wrong things at the cost of raw language power at the hand of experts (with increased complexity). The choice made is to me a valid one albeit I personally would like the power (one can always implement a java++ language on the JVM that does not have these restrictions).

Paul de Vrieze