Learn Java

Lesson 16Phase 2: Objects

Inheritance

Building a new class on top of an existing one, the is-a relationship, and what a subclass inherits.

By Updated 60 min

  • inheritance
  • extends
  • superclass
  • subclass
  • is-a
  • inherited members

You can now build a class, give it fields and methods, protect those fields, and construct an object in a valid state. Picture two classes you might write next, a Dog and a Cat, and you will notice something uncomfortable: most of what you would type into both is the same. Inheritance lets you write the shared part once and build both classes on top of it.

By the end of this lesson
  • Name the problem inheritance removes: the same fields and methods copied into several classes.
  • Read class Dog extends Animal and say what Dog gets from Animal, and what it does not.
  • Use the is-a test to decide whether extends fits.
  • Add new fields and methods in a subclass on top of what it inherits.
  • Chain constructors with super(...) and predict the order in which they run.
  • Choose between private and protected for a member that subclasses need.
From before
You already have classes with fields and methods (classes and objects, methods), constructors that build a valid object and hand over to each other with this(...), the four access levels including the protected one we set aside, and the this keyword and references. That is the whole toolkit for this lesson. A subclass will not change the behavior it inherits yet; this lesson is only about building one class on top of another.

Two classes that are almost the same#

Suppose we are writing a small program for an animal shelter. A dog has a name and an age, and it can eat and sleep. A cat has a name and an age, and it can eat and sleep too. Written separately, with everything we know so far, the two classes look like this.

Shelter.java
1public class Main {
2 public static void main(String[] args) {
3 Dog d = new Dog();
4 d.name = "Rex";
5 d.age = 3;
6 d.eat();
7 d.bark();
8
9 Cat c = new Cat();
10 c.name = "Mittens";
11 c.eat();
12 c.meow();
13 }
14}
15
16class Dog {
17 String name;
18 int age;
19
20 void eat() { System.out.println(name + " is eating."); }
21 void sleep() { System.out.println(name + " is asleep."); }
22 void bark() { System.out.println(name + " says woof."); }
23}
24
25class Cat {
26 String name;
27 int age;
28
29 void eat() { System.out.println(name + " is eating."); }
30 void sleep() { System.out.println(name + " is asleep."); }
31 void meow() { System.out.println(name + " says meow."); }
32}
Prints
›Rex is eating.
›Rex says woof.
›Mittens is eating.
›Mittens says meow.

Run it and it works. But look at what we typed. The name field, the age field, the eat method and the sleep method appear twice, character for character. The only real difference is that a dog barks and a cat meows. If the shelter later adds a weight field, we add it in two places and hope we never forget one. Add a Rabbit and a Hamster, and we are copying the same four members into every class.

Key idea

When several classes share the same fields and methods, copying the shared part into each one puts the same idea in many places, where the copies can quietly drift apart. Write what is common once.

You have felt the pull toward sharing before
When the same few lines showed up twice, you moved them into a method so the logic lived in one spot. Inheritance applies that instinct to whole classes: the shared fields and methods move into one class, and other classes build on it.

A subclass extends a superclass#

Here is the move. A dog and a cat are both, more generally, animals. So we write one class, Animal, that holds everything they share: the name, the age, and the eat and sleep methods. Then Dog is built on top of Animal with the keyword extends.

The class being built on is the superclass (here Animal). The class doing the extending is the subclass (here Dog). The subclass automatically gets, or inherits, the fields and methods of its superclass, exactly as if they had been typed inside it. You do not copy them. They are simply there.

Shelter.java
1public class Main {
2 public static void main(String[] args) {
3 Dog d = new Dog();
4 d.name = "Rex";
5 d.age = 3;
6
7 d.eat();
8 d.sleep();
9 d.bark();
10 }
11}
12
13class Animal {
14 String name;
15 int age;
16
17 void eat() { System.out.println(name + " is eating."); }
18 void sleep() { System.out.println(name + " is asleep."); }
19}
20
21class Dog extends Animal {
22 void bark() { System.out.println(name + " says woof."); }
23}
Prints
›Rex is eating.
›Rex is asleep.
›Rex says woof.

Read the Dog class. It declares no name, no age, no eat and no sleep; it only adds bark. Now step through and look at the memory view: the Dog object that d points at has a name field and an age field all the same, and main sets them, calls d.eat() and calls d.sleep() without trouble. The output is Rex is eating., Rex is asleep. and Rex says woof. A Dog object really has those fields and methods, because it inherited them from Animal.

What a subclass gets, and what it does not#

  • Every field and method of the superclass becomes part of each subclass object, and the subclass's code can use the ones that are not private.
  • A private field of the superclass is still inside every subclass object, but the subclass's code cannot name it. It has to go through the superclass's methods instead. The end of this page shows what to do about that.
  • Constructors are not inherited. Each class writes its own, and a subclass constructor calls its superclass's constructor, as you will see shortly.
  • A class extends exactly one class. Java has no extends Animal, Pet.

is-a: the test for whether inheritance fits

Inheritance describes an is-a relationship. A dog is an animal. A cat is an animal. That sentence has to be true in plain English for the inheritance to make sense. When it is true, the subclass is a more specific kind of the superclass, and everything true of the general thing stays true of the specific one.

Key idea

Before you write class B extends A, say “a B is an A” out loud. If that sentence is false, inheritance is the wrong tool, however much code it would save.

is-a, not has-a
A car has an engine, but a car is not an engine, so Car extends Engineis wrong even though both have fields. “Has-a” is solved by giving the class a field of that type (a Car with an Engine engine;field), which you already know how to do. “Is-a” is what extends is for.
Quick check
Which of these is a good place for extends?

Adding what makes the subclass special#

A subclass is not limited to what it inherits. It starts with everything from the superclass and adds its own fields and methods on top. The shared part lives in Animal once, and each subclass contributes only what is particular to it.

Shelter.java
1public class Main {
2 public static void main(String[] args) {
3 Dog d = new Dog();
4 d.name = "Rex";
5 d.age = 3;
6 d.breed = "Husky";
7
8 d.eat();
9 d.describe();
10 d.bark();
11 }
12}
13
14class Animal {
15 String name;
16 int age;
17
18 void eat() { System.out.println(name + " is eating."); }
19
20 void describe() {
21 System.out.println(name + " is " + age + " years old.");
22 }
23}
24
25class Dog extends Animal {
26 String breed;
27
28 void bark() {
29 System.out.println(name + " the " + breed + " says woof.");
30 }
31}
Prints
›Rex is eating.
›Rex is 3 years old.
›Rex the Husky says woof.

Step through it. The Dog object that d points at has three fields: name and age from Animal, and its own breed. The memory view shows them together because they all belong to this one object. Notice that bark reads name, which it inherited, and breed, which is its own, in the same line, with no difference between them. The output is Rex is eating., Rex is 3 years old. and Rex the Husky says woof.

Inheritance only goes one way
Dog can see and use Animal's members, but Animal knows nothing about Dog. The describe method in Animal cannot mention breed, because plenty of animals are not dogs. The general class must not depend on its specific subclasses. The subclass reaches up; the superclass never reaches down.

Constructors and the call to super#

So far we built the object the loose way: new Dog() and then a pile of assignment lines. You learned to do better with constructors, which set an object up in one valid step. The question is how constructors work once one class extends another. The answer is a new keyword, super.

Give Animal a constructor that takes a name, and give Dog a constructor that takes a name and a breed. The Dog constructor does not set name itself. The name field belongs to the Animal part of the object, so Dog hands the name up to Animal's constructor by calling super(name), and then sets its own breed.

Shelter.java
1public class Main {
2 public static void main(String[] args) {
3 Dog d = new Dog("Rex", "Husky");
4 d.describe();
5 d.bark();
6 }
7}
8
9class Animal {
10 String name;
11
12 Animal(String name) {
13 System.out.println("Animal constructor: setting name.");
14 this.name = name;
15 }
16
17 void describe() {
18 System.out.println("This animal is called " + name + ".");
19 }
20}
21
22class Dog extends Animal {
23 String breed;
24
25 Dog(String name, String breed) {
26 super(name);
27 System.out.println("Dog constructor: setting breed.");
28 this.breed = breed;
29 }
30
31 void bark() {
32 System.out.println(name + " the " + breed + " says woof.");
33 }
34}
Prints
›Animal constructor: setting name.
›Dog constructor: setting breed.
›This animal is called Rex.
›Rex the Husky says woof.

Step through it and read the narration. new Dog("Rex", "Husky") creates one Dog object, which already has both fields, name and breed, still null. Then it runs the Dog constructor, whose first line, super(name), runs the Animal constructor on that same object. You see Animal's message and the step that sets this object's name field to "Rex". Only then does the Dog constructor carry on, print its own message and set breed. The output is Animal constructor: setting name., Dog constructor: setting breed., This animal is called Rex. and Rex the Husky says woof.

This is called constructor chaining, and it always happens. There is only ever one object; the Animal constructor sets up the Animal part of it, and then the Dog constructor finishes the Dog part. A Dog is never left with a breed but no animal underneath.

Put the lines in orderChain.java
Put the lines in order so that building one dog prints Animal part: Rex, then Dog part: Husky, then Rex the Husky.

Tap a line, then tap where it should go. Or use the arrows.

When you do not write super#

If a constructor does not call super(...)itself, Java quietly calls the superclass's no-argument constructor, super(), before the constructor's own lines run. So the chain happens whether you write it or not. Here are three classes, each built on the one before, and none of the constructors calls super. Decide what the program prints before you run it.

Predict the outputPuppy.java
1public class Main {
2 public static void main(String[] args) {
3 Puppy p = new Puppy("Rex");
4 System.out.println("Made " + p.name);
5 }
6}
7
8class Animal {
9 String name;
10
11 Animal() {
12 System.out.println("Animal part built");
13 }
14}
15
16class Dog extends Animal {
17 Dog() {
18 System.out.println("Dog part built");
19 }
20}
21
22class Puppy extends Dog {
23 Puppy(String name) {
24 System.out.println("Puppy part built");
25 this.name = name;
26 }
27}

Puppy extends Dog, which extends Animal. What does new Puppy("Rex") print, and in what order?

It prints Animal part built, Dog part built, Puppy part built and then Made Rex. new Puppy("Rex") does start in the Puppy constructor, but before its first line runs, Java calls super(), which starts the Dog constructor, which in turn calls super() and starts the Animal constructor. The most general class finishes first, and each subclass adds its part after the class it is built on is ready.

Key idea

Building an object runs the constructors from the top of the family down: the superclass's constructor finishes before the subclass constructor uses the object, whether you write super(...) yourself or Java inserts super() for you.

Forgetting super when the superclass has no no-argument constructor
Java's quiet super() only works if the superclass has a constructor that takes no arguments. Once Animal only has Animal(String name), there is no Animal() to call, and a Dog constructor that leaves out super(name) does not compile. javac reports constructor Animal in class Animal cannot be applied to given types, with required: String and found: no arguments. The fix is to call super(name) yourself and pass the superclass what it needs.
ExerciseCat.java
Finish Cat. Its constructor must pass the name and age up to Animal with super(...) and store indoor in its own field. Then write describeHome() so that it prints Mittens lives indoors. for an indoor cat and Mittens lives outdoors. for an outdoor one, using the inherited name.

protected: open to subclasses#

In encapsulation you met four access levels and set one aside. private means “only inside this class”. publicmeans “anyone”. protected sits between them: a protected member can be used by its own class, by every class in the same package, and by subclasses, even subclasses in other packages.

That is the level a superclass often wants for fields its subclasses should use directly. If Animal's name were private, a Dog method could not read it by name, because private means this class only, and a subclass is a different class. Marking it protected opens it to subclasses.

Shelter.java
1public class Main {
2 public static void main(String[] args) {
3 Dog d = new Dog("Rex", "Husky");
4 d.bark();
5 }
6}
7
8class Animal {
9 protected String name;
10
11 Animal(String name) {
12 this.name = name;
13 }
14}
15
16class Dog extends Animal {
17 private String breed;
18
19 Dog(String name, String breed) {
20 super(name);
21 this.breed = breed;
22 }
23
24 void bark() {
25 System.out.println(name + " the " + breed + " barks.");
26 }
27}
Prints
›Rex the Husky barks.

Here name is protected, so Dog's bark method reads it without a problem, and the program prints Rex the Husky barks. Meanwhile breed is private to Dog, which is fine because no other class needs it.

private
protected
Usable only inside the same class.
Usable in the class, in its package, and in its subclasses.
A Dog cannot name a private field of Animal.
A Dog can use a protected field of Animal.
The strongest hiding. Your default.
A deliberate opening for subclasses.
Other classes in the same package: blocked.
Other classes in the same package: allowed.
protected in a one-file program
Because protected also opens a member to its package, and every class in these one-file programs shares one package, main could read d.name here too. The difference from package access only shows once a program is split into packages, which you will do in packages and imports.

There is another way to give subclasses what they need: keep the field private and offer a method that reads it. The subclass inherits the method, and the superclass stays in charge of its field.

Quick check
Animal declares private String name; and a public method getName() that returns it. Which line, written in a method of Dog extends Animal, compiles?
Do not reach for protected by reflex
protected loosens encapsulation: every present and future subclass can depend on that field directly, so you can no longer change it freely. Use it when subclasses truly need the access. When in doubt, keep the field private and offer a getter instead, so the superclass keeps control of how the value is read.
What inheritance cannot do yet
So far a subclass can only add to its superclass. It cannot yet change a method it inherited. A Dog stuck with Animal's general describe cannot say anything about its breed there. Replacing an inherited method with your own version is called overriding, and it is the next lesson: method overriding and super.

Try it

Answer each one in your head or on paper first, then open the answer.

1
In one sentence each, what is a superclass and what is a subclass, and which keyword connects them?
2
For each pair, say whether extends fits by reading it as “is-a”: Cat and Animal; Wheel and Car; Student and Person.
3
Look at the second shelter example, where Dog extends Animal and Dog only declares bark. Why does d.eat() work even though Dog never wrote an eat method?
4
In the super example, the output starts with Animal constructor: setting name. before Dog constructor: setting breed. Explain that order in one sentence.
5
A field name in Animal is marked private. A Dog extends Animal tries to read name directly in its own method. Does it compile? What one-word change fixes it? The program below already applies the fix, so it runs.
6
Animal has a constructor Animal(String name). Can you write new Dog("Rex") if Dog extends Animal declares no constructors of its own?

Practice

Work through these on your own. Each one builds on this page.

1
On paper, design a small family with one superclass and two subclasses (for example Vehicle with Car and Motorcycle). List which fields and methods belong in the superclass because they are shared, and which belong in each subclass because they are specific. Justify each placement with the is-a test.
2
Take the constructor-chaining example and add an int age field to Animal, set through the Animal constructor. Update both constructors so a Dog is created with a name, an age and a breed, with the age handled by super. Check that the constructor messages still print in the same order.
3
Write a three-level family: Shape, Rectangle extends Shape and Square extends Rectangle. Shape stores a String label, Rectangle adds width and height, and Square's constructor takes a label and one side, and passes the side up twice. Put a println in each constructor, predict the order for new Square("tile", 3), then run it.
4
Break the warning case on purpose: give Animal only the constructor Animal(String name), then write a Dog constructor that does not call super(name). Predict whether it compiles and explain why, using the silent super() Java tries to insert. Then fix it.

Further reading#

Spotted a mistake, or a part that did not make sense? Report it by email