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).
public class Pet {
private String name;
public Pet(String name) {
this.name = name;
}
}
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.
public class Thing {
private int id;
public Thing(int id) {
this.id = id;
}
public Thing(String id) {
this.id = Integer.parseint(id);
}
}
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.
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
.
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
.
public class Better {
private ArrayList<Thing> stuff;
public Better() {
stuff = new ArrayList<>();
}
public void addThing(Thing t) {
stuff.add(t);
}
}
public class Best {
private ArrayList<Thing> stuff = new ArrayList<>();
public void addThing(Thing t) {
stuff.add(t);
}
}
Room
classpublic class Room {
private ArrayList<String> things;
public Room() {
this.things = new ArrayList<>();
}
public void addThing(String t) {
things.add(t);
}
}
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
.
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
againpublic class Pet {
private String name;
public Pet(String name) {
this.name = name;
}
}
This class has one constructor that takes a String
.
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.
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 | ✅ |
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.
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.
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.
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.