Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java constructor chain direction

I realize there are special classes for which this general question doesn't apply, but for the simple ones, when we have multiple constructors, and the parameters of one are a clean subset of another, is it better to call the constructor with the longer list from the one with the shorter list, or vice versa? Why?

public class A {
    int x;
    int y;
    int z;

    public A() {
        this(0);
    }
    public A(int x) {
        this (x, 0);
    }
    public A(int x, int y) {
        this(x, y, 0);
    }

    public A(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
        // some setup stuff needed for all A
    }
}

Or

public class A {
    int x;
    int y;
    int z;

    public A(int x, int y, int z) {
        this(x, y);
        this.z = z;
    }

    public A(int x, int y) {
        this(x);
        this.y = y;
    }

    public A(int x) {
        this();
        this.x = x;
    }

    public A() {
        // some setup stuff needed for all A
    }
}
like image 265
Carl Manaster Avatar asked Jul 23 '15 16:07

Carl Manaster


People also ask

Does order matter in constructor chaining in Java?

Constructor chaining can be achieved in any order.

How can you do constructor Chaining in Java?

Constructor chaining is the process of calling a sequence of constructors. We can do it in two ways: by using this() keyword for chaining constructors in the same class. by using super() keyword for chaining constructors from the parent class.

Does order matter in constructor chaining select one?

Rules of Constructor Chaining An expression that uses this keyword must be the first line of the constructor. Order does not matter in constructor chaining.

What does this () mean in constructor chaining?

Java Constructor Chaining in the Same Class You can create multiple constructors in the same class, each with a different number of arguments that it accepts. To call one of the constructors in another constructor (of the same class), use the keyword this().


1 Answers

Have a look at the second variant:

public A(int x, int y, int z) {
    this(x, y);
    this.z = z;
}

public A(int x, int y) {
    this(x);
    this.y = y;
}

public A(int x) {
    this();
    this.x = x;
}

public A() {
    // some setup stuff needed for all A
}

Note that this “stuff needed for all A” is impossible to set up, if it requires the actual values of x, y, z. The only way to fix that, is to let the default constructor do that work using the default values of x, y, z and then overwrite its results in the calling constructor, using the specified non-default values. That’s a no-go if these setup work has noticeable side effects, but even without side effects it may also have a negative effect on the performance, considering the worst case, that the A(int x, int y, int z) constructor performs that work four times.

Besides that, there are (at least) three scenarios, where your second variant doesn’t work, even without such setup work:

  1. As already explained by Codebender, the parameter lists are not required to be a subset of each other.

    public A(TypeA a) {
        this(a, DEFAULT_B);
    }
    public A(TypeB b) {
        this (DEFAULT_A, b);
    }
    public A(TypeA a, TypeB b) {
        …
    }
    
  2. the fields might be final. Then, the last constructor in the chain, which does not invoke another constructor of this class, must initialize all final fields while the delegating constructors are not allowed to write into these final fields at all.

    public class A {
        final int x, y, z;
    
        public A() {
            this(0);
        }
        public A(int x) {
            this (x, 0);
        }
        public A(int x, int y) {
            this(x, y, 0);
        }
    
        public A(int x, int y, int z) {
            this.x = x;
            this.y = y;
            this.z = z;
            // optionally, some setup stuff needed for all A
        }
    }
    
  3. Some of the fields are defined in the superclass and required to be initialized via the constructor. Similarly to the final field initialization, only the last constructor in the chain can invoke the super constructor while the others can’t invoke a super constructor, so only a constructor knowing the appropriate values can be that last one.

    public class A extends B{
        int z;// B has x and y
    
        public A() {
            this(0);
        }
        public A(int x) {
            this (x, 0);
        }
        public A(int x, int y) {
            this(x, y, 0);
        }
    
        public A(int x, int y, int z) {
            super(x, y);
            this.z = z;
            // optionally, some setup stuff needed for all A
        }
    }
    

Since there are a lot of scenarios where the second variant doesn’t work, I would not use it in working scenarios either, as whenever something is merely a stylistic question, you should strive for consistency.

like image 186
Holger Avatar answered Sep 30 '22 20:09

Holger