Inheritance: building on a class
Inheritance, interfaces and polymorphism11 min readLesson 1 of 6
1.1 A class that extends another
Inheritance lets a class reuse fields and methods from another class. The child class is declared with extends, and the parent is called the superclass. A Car that extends Vehicle gets the fields and methods of Vehicle and can add its own.
The relationship should pass an IS-A test: a car is a vehicle, a savings account is an account. If the sentence sounds wrong, a field that holds the other object is usually a better design.
Java allows one direct superclass. Every class that does not extend another class extends Object, which supplies methods such as toString and equals.
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.name = "City";
car.start();
System.out.println(car.wheels);
}
}
class Vehicle {
String name;
int wheels = 4;
void start() {
System.out.println(name + " starts");
}
}
class Car extends Vehicle {
}Output
City starts 4
1.1.1 Constructors and super
Constructors are not inherited. A child constructor can call a parent constructor with super(arguments), and that call must be the first statement in the child constructor. If the child constructor does not call super(...), the compiler inserts a call to the parent's no-argument constructor.
The parent constructor runs before the child constructor body. Fields are initialized in that order, so a child constructor should not assume its own field assignments have already happened when the parent runs.
When the parent has no no-argument constructor, every child constructor must call the matching super(...) form. The compiler reports an error if no parent constructor can be called.
public class Main {
public static void main(String[] args) {
SavingsAccount account = new SavingsAccount("Asha", 500);
System.out.println(account.owner + " " + account.balance);
}
}
class Account {
String owner;
Account(String owner) {
this.owner = owner;
System.out.println("Account ready");
}
}
class SavingsAccount extends Account {
int balance;
SavingsAccount(String owner, int balance) {
super(owner);
this.balance = balance;
}
}Output
Account ready Asha 500
1.1.2 What is inherited
Public and protected members are inherited. Private members exist in the parent object but are not accessible by name in the child. A child can use a public or protected getter to read a private field.
Package-private members are inherited only when both classes sit in the same package. That rule matters after a project adds packages.
A child can add fields and methods, and it can hide a field with the same name. Field hiding is confusing. Use a different name or a getter instead. Method behavior is covered in the next lesson through overriding.
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat();
dog.bark();
}
}
class Animal {
protected String sound = "some sound";
void eat() {
System.out.println("Eating");
}
}
class Dog extends Animal {
void bark() {
System.out.println(sound + " woof");
}
}Output
Eating some sound woof
1.1.3 When inheritance fits
Inheritance works when the child is a specialized version of the parent and can stand in wherever the parent is expected. The child must honor the parent's promises: a method that returns a positive amount in the parent should not return a negative one in the child.
A class that only needs some helper behavior can hold an object of that class as a field. This is composition, and it keeps the two classes independent. Changing one class then has less effect on the other.
Deep chains are hard to follow. Two levels are common. A long chain usually means the responsibilities are mixed and could be split.
public class Main {
public static void main(String[] args) {
Report report = new Report(new Printer());
report.print();
}
}
class Printer {
void print(String text) { System.out.println(text); }
}
class Report {
private final Printer printer;
Report(Printer printer) { this.printer = printer; }
void print() { printer.print("Monthly report"); }
}Output
Monthly report
1.1.4 Superclass references
A variable of the parent type can refer to a child object: Vehicle vehicle = new Car();. The variable can call the methods declared by the parent. The object may have extra methods, but the compiler only allows calls that the declared type promises.
This is the beginning of polymorphism. The call is checked against the variable type and dispatched to the object at runtime. A later lesson covers overriding and casting in detail.
Use the parent type when the code does not need child-specific behavior. It keeps a method open to new subclasses without a signature change.
public class Main {
public static void main(String[] args) {
Vehicle vehicle = new Car();
vehicle.start();
}
}
class Vehicle {
void start() {
System.out.println("Engine on");
}
}
class Car extends Vehicle {
void honk() {
System.out.println("Honk");
}
}Output
Engine on
1.1.5 Practice
- Create
Animalwith a name field and aneatmethod, thenDog extends Animalwith abarkmethod. - Add a constructor to
Animaland call it fromDogwithsuper. - Add a private field to
Animalwith a getter, and read it fromDog. - Create a parent reference to a child object and call an inherited method.
- Add a
Collarclass and decide whetherDogshould extend it or hold it as a field. Use the IS-A test.
For step 5, write one sentence that uses IS-A. If the sentence is false, a field is the better design.
