Learn Java

Lesson 17Phase 2: Objects

Method overriding and super

Replacing inherited behavior, calling back up to the parent, and the @Override safety net.

By Updated 55 min

  • overriding
  • super
  • @Override
  • dynamic dispatch
  • overriding vs overloading

In inheritancea subclass got its parent's fields and methods for free. That is a fine deal when the inherited behavior is exactly what you want. But often it is almost right and not quite. A Dog is an Animal, yes, but it does not make a generic animal noise. It barks. This lesson is about a subclass saying: thank you for the method, I will take it from here.

By the end of this lesson
  • Replace an inherited method by redefining it in the subclass, and say what overriding means.
  • Use super.method() to run the parent's version from inside your own override.
  • Explain what @Override does and why it belongs on every override.
  • Follow the rules for an override, including why toString must be public.
  • Tell overriding apart from overloading without hesitating.
  • Predict which version of a method runs when a parent-typed variable points at a child object.
From before
This sits directly on top of inheritance: a subclass made with extends inherits the fields and methods of its superclass, and builds its parent part with super(...). You also need methods (parameters, return values and overloading) and object references (a variable holds a reference to an object, not the object itself).

The inherited method is almost right#

Here is where inheritance left off. An Animal can speak, and a Dog inherits that ability without writing a single new line. Watch what a dog actually says.

Main.java
1public class Main {
2 public static void main(String[] args) {
3 Animal a = new Animal();
4 Dog d = new Dog();
5
6 System.out.println(a.speak());
7 System.out.println(d.speak());
8 }
9}
10
11class Animal {
12 String speak() {
13 return "Some generic animal sound";
14 }
15}
16
17class Dog extends Animal {
18 // Dog adds nothing. It just inherits speak() from Animal.
19}
Prints
›Some generic animal sound
›Some generic animal sound

Both lines print the same thing. The dog inherited speakword for word, so it makes the generic noise. That is correct inheritance and it is also useless: nobody believes a dog says “Some generic animal sound”. The dog should stay an Animal in every other way and answer speak differently.

Key idea

Inheritance gives you the parent's behavior for free. Overriding lets you swap out one piece of that behavior while keeping the rest.

Overriding: redefine the method in the subclass#

To change what a dog says, write a method in Dog with the same name, the same parameters and the same return type as the one in Animal. When a subclass redefines an inherited method like this, it overridesthat method. The dog's version wins for dogs, and the animal's version still stands for plain animals.

Main.java
1public class Main {
2 public static void main(String[] args) {
3 Animal a = new Animal();
4 Dog d = new Dog();
5
6 System.out.println(a.speak());
7 System.out.println(d.speak());
8 }
9}
10
11class Animal {
12 String speak() {
13 return "Some generic animal sound";
14 }
15}
16
17class Dog extends Animal {
18 String speak() {
19 return "Woof";
20 }
21}
Prints
›Some generic animal sound
›Woof

Now the two lines differ. a.speak() runs the Animal version and prints the generic sound. d.speak() runs the Dog version and prints Woof. The Dog still inherits everything else from Animal; only the one method was replaced. The parent's speak is not deleted or damaged, and plain animals still use it.

For a dog, there is one speak()
Dog.speak() takes the place of Animal.speak() for every Dog object. A call such as d.speak()never runs both, and you never have to choose between them. Java runs the version in the object's own class when that class has one, and the inherited version when it does not.
Quick check
Animal has the method String speak(). Which method, written inside Dog, overrides it?

Calling back to the parent with super#

Sometimes you do not want to throw the parent's work away. You want to add to it. A cat makes its own sound, but suppose we still want the generic animal sound tucked inside the cat's answer. Java has a keyword for reaching the version you just overrode: super.

Inside an overriding method, super.speak() means “run the speakthat my parent class has, not my own”. Without super, writing speak()there would call the cat's own version again, which would call itself again, and so on until Java gives up with a StackOverflowError. The superprefix skips your own version and goes to the parent's.

Main.java
1public class Main {
2 public static void main(String[] args) {
3 Cat c = new Cat();
4 System.out.println(c.speak());
5 }
6}
7
8class Animal {
9 String speak() {
10 return "generic sound";
11 }
12}
13
14class Cat extends Animal {
15 String speak() {
16 return "Meow (" + super.speak() + ")";
17 }
18}
Prints
›Meow (generic sound)

Trace it by hand. c.speak() enters the Cat version. It builds a string starting with "Meow (", then reaches super.speak(), which jumps up to Animal.speak() and gets back "generic sound". The pieces join into Meow (generic sound), and that is what prints. The cat extended the parent's behavior instead of replacing it.

Key idea

Override to replace. Override and call superto extend. Replacing throws the old behavior away; extending wraps your own work around the parent's.

Here is that receipt, scrambled. Order matters inside the override: the parent's lines come first, then the extra one.

Put the lines in orderReceipt.java
Put the lines in order so that a PremiumReceipt prints the normal receipt first and then Thank you for being a member!. Each method belongs inside its own class.

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

You have met super once before
In inheritance you wrote super(...)with parentheses right after it, which calls the parent's constructor. Here super.speak(), with a dot, calls a parent's method. Same keyword, two uses: super(...) for the constructor, super.something() for a method.

Putting it together: Animal, Dog, Cat#

Here is the full cast in one program. Dog replaces speak outright. Cat extends it with super. A plain Animal keeps the original. Press Run and step through. Watch each speak() call land in a different method body even though all three calls look alike.

Main.java
1public class Main {
2 public static void main(String[] args) {
3 Animal generic = new Animal();
4 Dog rex = new Dog();
5 Cat lily = new Cat();
6
7 System.out.println(generic.speak());
8 System.out.println(rex.speak());
9 System.out.println(lily.speak());
10 }
11}
12
13class Animal {
14 String speak() {
15 return "Some animal sound";
16 }
17}
18
19class Dog extends Animal {
20 String speak() {
21 return "Woof!";
22 }
23}
24
25class Cat extends Animal {
26 String speak() {
27 return "Meow (" + super.speak() + ")";
28 }
29}
Prints
›Some animal sound
›Woof!
›Meow (Some animal sound)

The output is three different lines: Some animal sound, then Woof!, then Meow (Some animal sound). Three objects share one method name and behave three different ways. Each object decides which body runs, based on what it actually is.

Quick check
Puppy extends Dog, and Dog extends Animal. Animal's speak() returns "Some animal sound", Dog's returns "Woof", and Puppy's returns super.speak() + "!". What does new Puppy().speak() return?

The @Override safety net#

Overriding depends on matching the name and parameters exactly, and a small slip means you do not override at all. Here is a Dog whose author meant to override speak() but swapped two letters in the name. Decide what it prints before you run it.

Predict the outputTypo.java
1public class Main {
2 public static void main(String[] args) {
3 Dog d = new Dog();
4 System.out.println(d.speak());
5 }
6}
7
8class Animal {
9 String speak() {
10 return "Some generic animal sound";
11 }
12}
13
14class Dog extends Animal {
15 String spaek() {
16 return "Woof";
17 }
18}

The dog's method is spelled spaek. What does d.speak() print?

It prints Some generic animal sound. Java treats spaek as a brand new method with its own name, so a Dog now has two methods: the speak() it inherited and an unrelated spaek(). The call d.speak() finds the inherited one. There is no error and no warning, just the wrong sound. A wrong parameter list or a capital letter in the wrong place fails in the same silent way.

The fix is a one-word note to the compiler called an annotation. Writing @Override on the line above a method tells Java: this method is meant to override one from a parent class, so please check. If it does not override anything, the program does not compile, and the silent bug turns into an error you can fix in seconds. Here is the same dog with the annotation added, and what javac says about it:

Typo.java (does not compile)
read along
1public class Main {
2 public static void main(String[] args) {
3 Dog d = new Dog();
4 System.out.println(d.speak());
5 }
6}
7
8class Animal {
9 String speak() {
10 return "Some generic animal sound";
11 }
12}
13
14class Dog extends Animal {
15 @Override
16 String spaek() {
17 return "Woof";
18 }
19}
Output
Main.java:15: error: method does not override or implement a method from a supertype
    @Override
    ^
1 error

The compiler points straight at the annotation. (From Java 26 on, the message also names the method: spaek() in Dog does not override or implement a method from a supertype.) Fix the spelling to speak and the program compiles and barks.

Here is the cat from earlier with the annotation in place. Write every override this way from now on.

Cat.java
1public class Main {
2 public static void main(String[] args) {
3 Cat c = new Cat();
4 System.out.println(c.speak());
5 }
6}
7
8class Animal {
9 String speak() {
10 return "generic sound";
11 }
12}
13
14class Cat extends Animal {
15 @Override
16 String speak() {
17 return "Meow (" + super.speak() + ")";
18 }
19}
Prints
›Meow (generic sound)

The rules an override must follow#

Matching the name and the parameters is the main rule. The compiler checks two more, and both come up early in real code.

  • The return type must match the parent's. A void speak() cannot override a String speak(). An override may also return a more specific type, such as a Dog where the parent returns an Animal. That is called a covariant return type, and you will rarely need it at first.
  • An override cannot be less visible than the method it replaces. If the parent's method is public, the override must be public too. A protected method may become public in a subclass, but never private.

toString: a method every class inherits#

The second rule bites the first time you override a method you did not write yourself. Every class extends Object, as you saw in inheritance, and Object gives every object a toString() method that turns it into text. Whenever you print an object, println calls its toString(). Object's version gives the class name, an @ and a number, something like Book@1b6d3586, and the number usually changes from one run to the next. Override toString and your objects print something useful instead.

Book.java
1public class Main {
2 public static void main(String[] args) {
3 Book b = new Book("Dune", "Frank Herbert");
4 System.out.println(b);
5 System.out.println("On the shelf: " + b);
6 }
7}
8
9class Book {
10 private String title;
11 private String author;
12
13 Book(String title, String author) {
14 this.title = title;
15 this.author = author;
16 }
17
18 @Override
19 public String toString() {
20 return title + " by " + author;
21 }
22}
Prints
›Dune by Frank Herbert
›On the shelf: Dune by Frank Herbert

Step through it. Each println calls your toString(), and so does joining a book to a string with +. The two lines are Dune by Frank Herbert and On the shelf: Dune by Frank Herbert.

Notice the public in front. Object declares toString as public, so an override has to be public as well. A method with no access word at all, like the speak() methods on this page, is less visible than that, and javac rejects it:

Book.java (does not compile)
read along
1public class Main {
2 public static void main(String[] args) {
3 Book b = new Book("Dune", "Frank Herbert");
4 System.out.println(b);
5 System.out.println("On the shelf: " + b);
6 }
7}
8
9class Book {
10 private String title;
11 private String author;
12
13 Book(String title, String author) {
14 this.title = title;
15 this.author = author;
16 }
17
18 @Override
19 String toString() {
20 return title + " by " + author;
21 }
22}
Output
Main.java:19: error: toString() in Book cannot override toString() in Object
    String toString() {
           ^
  attempting to assign weaker access privileges; was public
1 error

“Weaker access privileges” means “less visible”. The fix is the one word public.

ExercisePlayer.java
Printing a Player shows something like Player@1b6d3586. Override toString so that a player prints as Ada has 91 points. Remember the annotation and the access word.

Overriding versus overloading#

Overriding and overloading sound alike, and people mix them up all the time. Both involve methods that share a name, but they are different ideas. One question keeps them apart: do the parameter lists match?

  • Overloading is several methods with the same name but different parameter lists. You met it in methods: area(int side) and area(int width, int height) are two overloads. Java picks one by looking at the arguments you pass, and it decides while compiling, before the program runs.
  • Overriding is one method, with the same name and parameters, redefined in a subclass. There is a parent version and a child version. Java picks one by looking at the object's actual class, and it decides while the program runs.
Overriding
Overloading
Same name, same parameters.
Same name, different parameter lists.
Needs a parent class and a subclass (extends).
Usually all in one class. A subclass may also add an overload next to a method it inherited.
The subclass version replaces the parent's for that object.
The versions sit side by side, and every one of them stays callable.
Chosen while the program runs, by the object's real class.
Chosen while compiling, by the argument types.
Dog.speak() replacing Animal.speak()
show(int) next to show(String)

Here is overloading on its own, with no inheritance in sight. One class has three methods named show, each taking a different parameter type. Step through it and watch which show each call jumps into.

Printer.java
1public class Main {
2 public static void main(String[] args) {
3 Printer p = new Printer();
4 p.show(42);
5 p.show("hello");
6 p.show(3.5);
7 }
8}
9
10class Printer {
11 void show(int x) {
12 System.out.println("int: " + x);
13 }
14 void show(String x) {
15 System.out.println("String: " + x);
16 }
17 void show(double x) {
18 System.out.println("double: " + x);
19 }
20}
Prints
›int: 42
›String: hello
›double: 3.5

It prints int: 42, String: hello and double: 3.5. Java matched each call to an overload by the type of its argument: 42 is an int, "hello" is a String and 3.5 is a double.

Quick check
SavingsAccount extends Account, and Account has void deposit(double amount). Which of these is overriding?

Who decides which version runs?#

This page keeps saying “the object decides”. Here is exactly what that means. When you call thing.speak(), the compiler checks the declared type of thing to make sure it has a speak() at all. Which version runs is settled later, while the program runs: Java looks at the object thingpoints to at that moment, finds that object's class, and runs that class's version of speak. This choice at run time is called dynamic dispatch.

The clearest way to see it is to point a parent-typed variable at a child object. Put a row of animals in one array whose declared type is Animal, then call speak() on each in one ordinary loop. The loop body never mentions Dog or Cat, yet each call lands in the right method.

Main.java
1public class Main {
2 public static void main(String[] args) {
3 Animal[] zoo = new Animal[3];
4
5 Animal thing = new Animal();
6 thing.name = "Thing";
7 Dog rex = new Dog();
8 rex.name = "Rex";
9 Cat lily = new Cat();
10 lily.name = "Lily";
11
12 zoo[0] = thing;
13 zoo[1] = rex;
14 zoo[2] = lily;
15
16 for (int i = 0; i < zoo.length; i = i + 1) {
17 System.out.println(zoo[i].name + " says " + zoo[i].speak());
18 }
19 }
20}
21
22class Animal {
23 String name;
24 String speak() {
25 return "...";
26 }
27}
28
29class Dog extends Animal {
30 @Override
31 String speak() {
32 return "Woof";
33 }
34}
35
36class Cat extends Animal {
37 @Override
38 String speak() {
39 return "Meow (" + super.speak() + ")";
40 }
41}
Prints
›Thing says ...
›Rex says Woof
›Lily says Meow (...)

Every slot of zoo is declared as an Animal, so the loop can treat them all the same way. But when zoo[i].speak() runs, Java checks the real object in that slot. Slot 0 holds a plain animal, so it prints Thing says .... Slot 1 holds a dog, so it prints Rex says Woof. Slot 2 holds a cat, so it prints Lily says Meow (...). The loop has one method call, and Java chooses its version object by object as the program runs.

Dynamic dispatch also applies when the call is written inside the parent's own code. Below, Animal has a describe() method that calls speak(), and Dog overrides only speak(). Decide what the two lines print.

Predict the outputDescribe.java
1public class Main {
2 public static void main(String[] args) {
3 Animal generic = new Animal("Thing");
4 Dog rex = new Dog("Rex");
5
6 System.out.println(generic.describe());
7 System.out.println(rex.describe());
8 }
9}
10
11class Animal {
12 String name;
13
14 Animal(String name) {
15 this.name = name;
16 }
17
18 String speak() {
19 return "...";
20 }
21
22 String describe() {
23 return name + " says " + speak();
24 }
25}
26
27class Dog extends Animal {
28 Dog(String name) {
29 super(name);
30 }
31
32 @Override
33 String speak() {
34 return "Woof";
35 }
36}

Which speak() does describe() call when it runs for Rex?

The second line is Rex says Woof. describe() lives in Animal and Dog never touched it, yet the speak() inside it ran the dog's version. A bare call like speak() inside a method means this.speak(), a call on the object the method is running for, and here that object is a Dog. Wherever the call is written, the object decides. Open it in the playground and step through: the second describe() jumps from Animal's code into Dog's speak().

Key idea

An ordinary call to an overridden method runs the object's own version, wherever the call is written and however the variable is declared.

ExerciseTeenAccount.java
A TeenAccount works like an Account, except that it refuses to hand out more than 100 at a time. Override withdraw in TeenAccount: for an amount over 100, print Limit is 100 and change nothing; for any other amount, let Account's withdraw do the work.

Overloads look at the variable, overrides look at the object#

The two kinds of choice can meet in one program. Below, Keeper has two overloads of feed: one for any Animal and one for a Dog. The variable a holds a dog, but its declared type is Animal. Guess which feed the call lands in, then step through and watch.

Keeper.java
1public class Main {
2 public static void main(String[] args) {
3 Keeper k = new Keeper();
4 Animal a = new Dog();
5
6 k.feed(a);
7 a.speak();
8 }
9}
10
11class Keeper {
12 void feed(Animal x) {
13 System.out.println("feed(Animal) runs");
14 }
15
16 void feed(Dog x) {
17 System.out.println("feed(Dog) runs");
18 }
19}
20
21class Animal {
22 void speak() {
23 System.out.println("Some animal sound");
24 }
25}
26
27class Dog extends Animal {
28 @Override
29 void speak() {
30 System.out.println("Woof");
31 }
32}
Prints
›feed(Animal) runs
›Woof

It prints feed(Animal) runs, then Woof. The overload is picked while compiling, and all the compiler knows about a is its declared type, Animal, so feed(Animal) runs even though the object is a dog. The override is picked while the program runs, from the real object, so speak() barks.

The same rule holds for classes you did not write. Object is the parent of every class, so an Object variable can hold any object, a String included. Below, word and thing point at the very same string.

Predict the outputOverloads.java
1public class Main {
2 public static void main(String[] args) {
3 Printer p = new Printer();
4 String word = "hello";
5 Object thing = word;
6
7 p.describe(word);
8 p.describe(thing);
9 }
10}
11
12class Printer {
13 void describe(Object x) {
14 System.out.println("Object: " + x);
15 }
16
17 void describe(String x) {
18 System.out.println("String: " + x);
19 }
20}

Both calls pass the same "hello" object. Which describe runs each time?

The first call prints String: hello and the second prints Object: hello. The object is the same both times. Only the declared type of the variable changed, and that alone decided which overload ran.

What is still ahead
This page pointed Animal variables at dogs and cats without comment. Why that is always allowed (it is called upcasting), and how to get from an Animal back to a Dog when you need to, are part of polymorphism. For overriding, it is enough to know that a parent-typed variable can hold a child object.

Try it

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

1
In one sentence, what does it mean for a subclass to override a method? Name the two things that must match for it to count as an override.
2
The Cat class has String speak() returning "Meow (" + super.speak() + ")", and Animal.speak() returns "generic sound". What does new Cat().speak() return, and what would go wrong if we wrote plain speak() instead of super.speak()?
3
Someone writes String speak(String mood) in Dog, hoping to override Animal's String speak(). Did they override it? What single line would have caught the mistake?
4
Label each as overriding or overloading. (a) Two methods named add in one class, one taking two ints, one taking two doubles. (b) SavingsAccount redefines withdraw() from its parent Account with the same signature.
5
In the zoo example, the loop holds three objects in an Animal[]: a plain Animal named Thing, a Dog named Rex, a Cat named Lily. Why does zoo[i].speak() print three different sounds even though every slot is declared as Animal?

Practice

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

1
In the playground, build Animal with String speak() returning "...", then add three subclasses: Dog returning "Woof", Cow returning "Moo", and Cat returning "Meow (" + super.speak() + ")". Mark each override with @Override. Make one of each in main and print all four sounds. Confirm the cat's line shows the parent's sound inside the parentheses.
2
Build a three-level family. Vehicle has String describe() returning "A vehicle". Car extends Vehicle and returns the parent's text plus " with 4 wheels". ElectricCar extends Car and returns the parent's text plus ", battery powered". Use super.describe() in both overrides, print an ElectricCar's description, and confirm it reads A vehicle with 4 wheels, battery powered. Then explain which describe() each super reached.
3
On paper, write two short methods that overload each other and two that override each other. For each pair, state in one line how Java decides which version to run, and when that decision is made (compile time or run time).
4
In your own words, in three or four sentences, explain why a single @Override line is worth typing even though a correct program runs the same with or without it. Give a concrete example of a mistake it would catch.

Further reading#

Short guides on this topic

Quick answers to what people search for about it, with code to run.

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