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 BasithUpdated 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.
- Name the problem inheritance removes: the same fields and methods copied into several classes.
- Read
class Dog extends Animaland say whatDoggets fromAnimal, and what it does not. - Use the is-a test to decide whether
extendsfits. - 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
privateandprotectedfor a member that subclasses need.
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.
public class Main { public static void main(String[] args) { Dog d = new Dog(); d.name = "Rex"; d.age = 3; d.eat(); d.bark(); Cat c = new Cat(); c.name = "Mittens"; c.eat(); c.meow(); }} class Dog { String name; int age; void eat() { System.out.println(name + " is eating."); } void sleep() { System.out.println(name + " is asleep."); } void bark() { System.out.println(name + " says woof."); }} class Cat { String name; int age; void eat() { System.out.println(name + " is eating."); } void sleep() { System.out.println(name + " is asleep."); } void meow() { System.out.println(name + " 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 ideaWhen 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.
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.
public class Main { public static void main(String[] args) { Dog d = new Dog(); d.name = "Rex"; d.age = 3; d.eat(); d.sleep(); d.bark(); }} class Animal { String name; int age; void eat() { System.out.println(name + " is eating."); } void sleep() { System.out.println(name + " is asleep."); }} class Dog extends Animal { void bark() { System.out.println(name + " 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
privatefield 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 ideaBefore 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.
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.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.
public class Main { public static void main(String[] args) { Dog d = new Dog(); d.name = "Rex"; d.age = 3; d.breed = "Husky"; d.eat(); d.describe(); d.bark(); }} class Animal { String name; int age; void eat() { System.out.println(name + " is eating."); } void describe() { System.out.println(name + " is " + age + " years old."); }} class Dog extends Animal { String breed; void bark() { System.out.println(name + " the " + breed + " 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.
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.
public class Main { public static void main(String[] args) { Dog d = new Dog("Rex", "Husky"); d.describe(); d.bark(); }} class Animal { String name; Animal(String name) { System.out.println("Animal constructor: setting name."); this.name = name; } void describe() { System.out.println("This animal is called " + name + "."); }} class Dog extends Animal { String breed; Dog(String name, String breed) { super(name); System.out.println("Dog constructor: setting breed."); this.breed = breed; } void bark() { System.out.println(name + " the " + breed + " 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.
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.
public class Main { public static void main(String[] args) { Puppy p = new Puppy("Rex"); System.out.println("Made " + p.name); }} class Animal { String name; Animal() { System.out.println("Animal part built"); }} class Dog extends Animal { Dog() { System.out.println("Dog part built"); }} class Puppy extends Dog { Puppy(String name) { System.out.println("Puppy part built"); this.name = name; }}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 ideaBuilding 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 insertssuper()for you.
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.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.
public class Main { public static void main(String[] args) { Dog d = new Dog("Rex", "Husky"); d.bark(); }} class Animal { protected String name; Animal(String name) { this.name = name; }} class Dog extends Animal { private String breed; Dog(String name, String breed) { super(name); this.breed = breed; } void bark() { System.out.println(name + " the " + breed + " 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.
Dog cannot name a private field of Animal.Dog can use a protected field of Animal.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.
Animal declares private String name; and a public method getName() that returns it. Which line, written in a method of Dog extends Animal, compiles?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.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.
extends fits by reading it as “is-a”: Cat and Animal; Wheel and Car; Student and Person.Dog extends Animal and Dog only declares bark. Why does d.eat() work even though Dog never wrote an eat method?super example, the output starts with Animal constructor: setting name. before Dog constructor: setting breed. Explain that order in one sentence.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.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.
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.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.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.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#
- Inheritance, the dev.java tutorial, including what a subclass can do and private members in a superclass
- Using the Keyword super, from dev.java, with subclass constructors
- Constructor bodies in the Java Language Specification, section 8.8.7, the exact rules for super(...), this(...) and the lines before them
- JEP 513: Flexible Constructor Bodies, the Java 25 change that allows lines before super(...)
Spotted a mistake, or a part that did not make sense? Report it by email