Inheritance
extends, super, constructor chaining, the Object class, final classes and composition vs inheritance.
Many classes share a lot in common. A SavingsAccount and a CurrentAccount both have a number, an owner and a balance; they differ only in a few rules. Copy-pasting the shared code into both classes means fixing every bug twice. Inheritance lets one class build on another: the new class (the subclass or child) automatically gets the fields and methods of the existing one (the superclass or parent), and then adds or changes what it needs.
extends: an "is-a" relationship#
Dog extends Animal reads as "a Dog is an Animal". Every Dog object has a name field and an eat() method without declaring them. This "is-a" test is the most important rule of inheritance: if the sentence sounds wrong ("a Car is an Engine"), inheritance is the wrong tool.
Key facts:
- Java has single inheritance of classes: a class can
extendonly one parent. (A class can implement many interfaces, covered later.) - Inheritance is transitive: if
Puppy extends Dog, aPuppyis also anAnimal. - Constructors are not inherited. Each class declares its own.
privatemembers exist inside the child object but the child cannot touch them directly.
Access and inheritance#
Combining inheritance with encapsulation, we keep fields private in the parent and give subclasses methods to work with:
super(...): constructor chaining#
When you create a Dog, Java first builds the Animal part of the object, then the Dog part. A subclass constructor calls a parent constructor with super(...), which must be the first statement:
Notice that SavingsAccount never touches balance directly. It goes through deposit() and getBalance(), so the parent's validation still applies.
The implicit super()
If you don't write super(...) (or this(...)), the compiler inserts super();, a call to the parent's no-argument constructor. If the parent does not have one, you get a compile error:
Fix it by passing the required arguments: Student() { super("Unknown"); }.
Construction order across several levels
The chain always runs top-down: the most general part is ready before the specific part is built.
Overriding a method (a first look)#
A subclass can replace an inherited method by declaring one with the same signature. Mark it with @Override so the compiler checks you really are overriding. Use super.method() to call the parent's version:
Look closely at the second line. Product.describe() calls finalPrice(), and because the object is really a DiscountedProduct, the overridden finalPrice() runs. That behaviour is called polymorphism, and it gets the whole next lesson.
The Object class: everyone's parent#
Every class that doesn't say extends implicitly extends java.lang.Object. So every object in Java has these methods (among others):
toString() is public in Object, so the override must also be public (an override cannot reduce visibility). equals and hashCode have their own lesson later in the course.
final: stopping inheritance#
- A
finalclass cannot be extended.String,IntegerandLocalDateare final, which protects their immutability. - A
finalmethod cannot be overridden, useful when a subclass must not change a critical rule.
Design advice from Effective Java: "Design and document for inheritance or else prohibit it." If you never meant a class to be extended, making it
finalis a perfectly good choice.
Composition over inheritance#
Inheritance is powerful but creates tight coupling: the child depends on the parent's internals, and a change to the parent can silently break every subclass. Often a better design is composition, where a class holds a reference to another object and delegates work to it.
With composition you can swap in an ElectricMotor later, test Car with a fake engine, and Car exposes only the methods you choose. A quick guide:
A classic warning: class Stack extends ArrayList would expose add(index, x) and remove(index), letting anyone break the stack rules. A Stack that contains a list exposes only push, pop and peek.
Common mistakes#
- Using inheritance just to reuse code when there is no real "is-a" relationship.
- Forgetting that the parent needs a no-argument constructor when the child doesn't call
super(...). - Putting
super(...)anywhere but the first line of the constructor. - Making parent fields
protectedso children can poke at them; preferprivatefields plus methods. - Forgetting
@Overrideand accidentally creating a new method (for exampletostring()with a lower-case s).
What's next#
You have seen that the object's real type decides which overridden method runs. Next, polymorphism explains exactly how that works, along with overloading, upcasting, downcasting and instanceof.
Check your understanding
Quick quiz
1.A subclass constructor does not call
super(...)explicitly. What does Java do?2.Which members does a subclass in a different package inherit and access directly?
3.You want a
Carto have anEngine. What relationship fits best?
Finished reading?
Mark this lesson complete to track your progress.