When talking about inheritance it is important to understand how objects in a hierarchy are created. For this section, let’s assume we have the following 2 classes:
We’ll have a simplified version of our Light
class that only provides us with the turnOn
/turnOff
functionality.
public class Light {
protected boolean on;
public Light(boolean isLigthOn) {
System.out.println("Light constructor");
on = isLigthOn;
}
public void turnOn() {
on = true;
}
public void turnOff() {
on = false;
}
public void printLightDetails() {
System.out.println("The light is on? " + on);
}
}
You can see that the Light
class has one constructor defined that has a boolean parameter as an input that defines if the light is on
or off
when it’s being created.
And we’ll add a child class called DimmableLight
that will allow us to change the power
/brightness of the light.
We’ll start with the basics of our class, no constructors added yet:
The example code in the project already has the constructors added as that’s the final version of the example. If you’re following this section and want to give this a try, just delete the DimmableLight constructors that have been defined.
|
public class DimmableLight extends Light {
private int power;
public void setPower(int power) {
if (power < 0 || power > 100) {
System.out.println("Invalid new power provided, expected 0-100 but got: " + power);
}
if (power == 0) {
turnOff();
} else {
turnOn();
}
this.power = power;
}
public int getPower() {
return power;
}
public void printDimmableLightDetails() {
System.out.println("The dimmable light is on? " + on);
if (on) {
System.out.println("The dimmable light power is: " + power);
}
}
}
With the code above, we are defining a new DimmableLight
class as a children class of Light
that adds new state (power
) and a couple of new behaviors to our Light
class.
However, when you add the code above in your IDE as it is, you will get a compilation error stating that the "[…] super constructor Light() is undefined […]".
What does this error mean?
If you recall the section about constructors, you’ll note that our DimmableLight
will have a constructor automatically added by the compiler because we haven’t defined any constructors. This implicit constructor that is added by the compiler will have these characteristics:
-
It is
public
-
It doesn’t have any parameters
-
It invokes a parameter-less constructor in its superclass
As our super class Light
doesn’t have a parameter-less constructor, we get a compilation error. We need to resolve this and invoke a constructor in our superclass that exists.
Invoking super class constructors
In the section about constructors we talked about how to invoke a constructor of the same class using the this(…)
keyword. In a similar way, we can invoke a specific constructor of the parent class (a.k.a superclass) using the super(…)
keyword.
For example, in our DimmableLight
we’ll create two separate constructors as follows:
public class DimmableLight extends Light {
//...
public DimmableLight() {
super(false);
}
public DimmableLight(boolean isLigthOn, int initialPower) {
super(isLigthOn);
System.out.println("DimmableLight constructor");
setPower(initialPower);
}
//...
}
Adding just one of these two constructors would be enough, but we’re adding 2 to showcase a couple of things. |
After we add a constructor to the DimmableLight
class that invokes a valid constructor of its super class, we’ll see that our compilation errors are gone and we should be able to run our code.
Similar to the this(…)
keyword, if the super(…)
keyword is being used, then the super(…)
call must be the first line in a constructor. This means you can’t use both this(…)
and super(…)
in a single constructor.
However, we can use both this(…)
and super(…)
in different constructors. For example, we could change our first constructor as follows and achieve the same result:
public DimmableLight() {
this(false, 0);
}
public DimmableLight(boolean isLigthOn, int initialPower) {
super(isLigthOn);
System.out.println("DimmableLight constructor");
setPower(initialPower);
}
In both cases, our dimmable light will be off initially with a power of 0.
How does Java call my constructors?
All of the above implies that Java has an order of constructing objects and that it expects a subclass to invoke a constructor of its superclass during its creation process. If the compiler detects that this isn’t happening, like in our first example above, it will give a compilation error.
During runtime, when we create a new instance of DimmableLight
, for example if we do, new DimmableLight(true, 50)
, Java will always create the superclass instance first and then the subclass instance.
Our class hierarchy currently looks like this: DimmableLight
→ Light
→ java.lang.Object
In our case, the first object created by Java will be an instance of Object
as it is the root of all classes in Java and is the superclass of Light
. The second object is of type Light
and that last one will be our DimmableLight
instance.
Let’s give it a try:
In the example code run the JavaInheritanceAndConstructorsApp .
|
DimmableLight dimmableLight = new DimmableLight(true, 50);
dimmableLight.printDimmableLightDetails();
Output:
Light constructor
DimmableLight constructor
The dimmable light is on? true
The dimmable light power is: 50
In the example above, we can see that our Light
constructor is invoked before our DimmableLight
constructor as we expected.
Constructors with no this(…)
and no super(…)
defined
The use of this(…)
or super(…)
in a constructor is optional. If we don’t specify either in a constructor, then the compiler will automatically add a default super()
call. This means, it will expect to be able to invoke a parameter-less constructor in the super class.
This means that adding a constructor like:
public DimmableLight() {
}
is equivalent to adding:
public DimmableLight() {
super();
}