Java Notes

Constructor Puzzle

This class compiles without a problem

  1 
  2 
  3 
  4 
  5 
  6 
  7 
  8 
  9 
/////////////////// class WITHOUT a parameterless constructor.//////////////////

class Parent {
    int _x;

    Parent(int x) {   // Constructor with a parameter.
        _x = x;
    }
}

But why won't this subclass compile?

  1 
  2 
  3 
  4 
  5 
  6 
  7 
  8 
////////////////// Why won't this class compile? ///////////////////////////////
class Child extends Parent {
    int _y;

    Child(int y) {  // This line mysteriously generates an error!
        _y = y;
    }
}

Hint

The error message from the compiler is:

   Child.java:5: cannot find symbol symbol : constructor Parent()

But you defined a constructor for Parent, and anyway, you didn't even call the Parent constructor, so what's the problem with line 5?

Constructor chaining - implicit or explicit constructor call as first statement

Before you can initialize an object in a constructor, the object's parent constructor must be called first. If you don't write an explicit call to the super() constructor, Java automatically inserts one in your constructor. The compiler automatically inserts superclass constructor calls in both constructors.

    Parent(int x) {
        super();  // Compiler inserts this statement to call Object().
        _x = x;
    }

This is fine because there is a parameterless constructor for Object. However, when the Child class constructor is modified to call its superclass constructor, there's a problem.

    Child(int y) {
        super();  // Compiler inserts this statement to call Parent().
        _y = y;
    }

No parameterless constructor

The problem is that the Parent class has no parameterless constructor. When any constructor is explicitly defined (as in Parent above), the compiler no longer produces a default parameterless constructor.

Possible solution 1 - Explicit constructor call

Usually the best solution is for the subclass constructor to explicitly call the superclass constructor so that its fields are correctly initialized.

    Child(int y) {
        super(-666);  // Make explicit call to superclass constructor.
        _y = y;
    }

Possible solution 2 - Define a parameterless constructor

But no invalid values please. Advice you will sometimes see is to always define a parameterless constructor, so that this problem doesn't occur. But an essential element of data encapsulation is that the value of a object can be guaranteed to be a legal value. A constructor insures that the initial value of an object is valid. Many objects can't be given a valid value with a default constructor. This peculiar semantic-free example Parent class might be changed to have a parameterless constructor as follows (supposing that a value of -666 makes sense for this class):

  1 
  2 
  3 
  4 
  5 
  6 
  7 
  8 
  9 
 10 
 11 
 12 
 13 
/////////////////// class with parameterless constructor. //////////////////////

class Parent {
    int _x;

    Parent() {        // Parameterless constructor
        this(-666);   // Call other constructor with default value.
    }

    Parent(int x) {   // Constructor with a parameter.
        _x = x;
    }
}

Example class where a default constructor would be a disaster

It's common to have a class with no legal default values. For example, what should the default constructor do for this class?

  1 
  2 
  3 
  4 
  5 
  6 
  7 
  8 
  9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
// A parameterless constructor would be wrong here.
public class Student2 {
    private String firstName; // First name
    private String lastName;  // Last name
    private int    id;        // Student id
    
    //====================================================== constructor
    public Student2(String fn, String ln, int idnum) {
        firstName = fn;
        lastName  = ln;
        id        = idnum;
    }
    
    //======================================== parameterless constructor
    public Student2() {
        ????              // No possible legal values.
    }
    
    //========================================================== setters
    public void setID(int id) {
        this.id = id;
    }
    
    . . .
}

If you defined a default constructor for Student2, what would you assign to the fields - null for the names and zero for the id? Whatever you choose it's illegal. There are setters so you hope that the user will set the fields to legal values. Or you will check for the illegal initial values and throw an exception or give some other error when there is an attempt to use them. Perhaps there are times when this is acceptible in a small one-person project, but it's a disaster waiting to happen in anything larger.

Never define a parameterless constructor unless it can create a valid object.