Inheritance and constructors

A class must have a constructor

Remember: The job of the constructor is to make sure the object is initialized and ready to use (meaning you can call methods on it).

Simple constructor

public class Pet {

  private String name;

  public Pet(String name) {
    this.name = name;
  }
}

No-arg constructor

public class RandomChoice {

  private String choice;

  public RandomChoice() {
    if (Math.random() < 0.5) {
      choice = "heads";
    } else {
      choice = "tails";
    }
  }
}

Here we write an explicit constructor even though we don’t need any arguments because we need to do something complex to initialize our value.

Two constructors

public class Thing {

  private int id;

  public Thing(int id) {
    this.id = id;
  }

  public Thing(String id) {
    this.id = Integer.parseint(id);
  }
}

Where's the constructor?

public class Widget {

  private static int nextSerialNumber = 0;

  private int serialNumber = nextSerialNumber++;

}

In this case the default no-arg constructor is added for us automatically.

The code that initializes serialNumber will run in that constructor.

Don’t do this

public class Bad {

  private ArrayList<Thing> stuff;

  public void setUpStuff() {
    stuff = new ArrayList<>();
  }

  public void addThing(Thing t) {
    stuff.add(t);
  }
}

After the constructor runs, stuff isn’t initialized and will still have its default value of null.

What happens here?

Bad b = new Bad();
b.addThing(new Thing(123));

After the object has been constructed, it’s not actually ready to use as stuff is still null.

The call to addThing will result in a NullPointerException.

Do this instead

public class Better {

  private ArrayList<Thing> stuff;

  public Better() {
    stuff = new ArrayList<>();
  }

  public void addThing(Thing t) {
    stuff.add(t);
  }
}

Or even better

public class Best {

  private ArrayList<Thing> stuff = new ArrayList<>();

  public void addThing(Thing t) {
    stuff.add(t);
  }
}

Now let’s think about inheritance

A Room class

public class Room {
  private ArrayList<String> things;

  public Room() {
    this.things = new ArrayList<>();
  }

  public void addThing(String t) {
    things.add(t);
  }
}

Extend Room

public class Kitchen extends Room {

  private int numberOfCabinets;

  public Kitchen(int numberOfCabinets) {
    this.numberOfCabinets = numberOfCabinets;
    addThing("Sink");
    addThing("Stove");
    addThing("Fruit bowl");
  }
}

For this to work, the things variable in Room needs to be initialized.

Which happens in the constructor defined in Room.

we could also have written this

public class Kitchen extends Room {

  private int numberOfCabinets;

  public Kitchen(int numberOfCabinets) {
    super(); // explicitly call the constructor
    this.numberOfCabinets = numberOfCabinets;
    addThing("Sink");
    addThing("Stove");
    addThing("Fruit bowl");
  }
}

Either way, the first thing that has to happen in a constuctor is to give the parent constructor a chance to initialize it’s part of the object.

Pet again

public class Pet {

  private String name;

  public Pet(String name) {
    this.name = name;
  }
}

This class has one constructor that takes a String.

Extend Pet

public Dog extends Pet {

  public Dog(String name) {
    super(name);
  }
}

In this case we have to write a constructor and we have to explicitly call the parent constructor.

The fact that Pet only has one constructor that takes an argument strongly implies that Dog’s constructor will have to take the same argument.

Though we could do this

public Dog extends Pet {

  public Dog() {
    super("Fido");
  }

  public Dog(String name) {
    super(name);
  }
}

This class has two constructors.

The no-arg constructor still has to invoke the parent constructor. It provides a default value for the required argument.

Parent / Child No constructor Explicit no-arg Constructor with args
No constructor
Without super
With super Ok Ok

The two things you don’t have to explicitly write:

  • A no-arg constructor if that’s the only constructor your class needs and if there’s nothing that needs to be initialized by running code.

  • A call to a parent’s no-arg constructor.

To review: facts about constructors

  • All classes must have at least one constructor.

  • If you do not write a constructor, the compiler will add a no-argument constructor for you.

  • All constructors must, as the first thing they do, invoke a constructor from their parent class.

  • If a constructor does not explicitly invoke a parent constructor with super, the compiler will insert a call to super(), i.e. to a no-argument constructor.

These facts have some important implications

  • A constructor in a class whose parent class does not have a no-arg constructor must explicitly invoke one of the constructors the parent class does have.

  • When extending a class without a no-arg constructor you will have to write a constructor in order to invoke one of the parent's constructors.

The point

Remember that the job of a constructor is to make sure an object is properly initialized and ready to use.

These rules work to ensure that that stays true even in the face of inheritance.