A Beginner’s Guide to Overriding Methods
A list of tips and tricks to overriding methods and using virtual method invocation in Java.
Join the DZone community and get the full member experience.
Join For FreeDon't worry about how the Oracle Certified Professional (OCP) Java SE 7 Programmer II Certification can grill you on overriding methods in Java.
In this article, excerpted from OCP Java SE 7 Programmer II Certification Guide, I'll cover overriding methods and virtual method invocation, including the tricks and traps that you might see on the exam.
Do you celebrate a festival or an event in exactly the same manner as celebrated by your parents? Or have your modified it? Perhaps you celebrate the same festivals and events, but in your own unique manner. In a similar manner, classes can inherit behavior from other classes. But they can redefine the behavior that they inherit — this is also referred to as method overriding.
Method overriding is an object-oriented programming (OOP) language feature that enables a derived class to define a specific implementation of an existing base class method to extend its own behavior. A derived class can override an instancemethod defined in a base class by defining an instance method with the same method signature or method name and number and types of method parameters. Overridden methods are also synonymous with polymorphic methods. The static methods of a base can’t be overridden, but they can be hidden by defining methods with the same signature in the derived class.
A method that can be overridden by a derived class is called a virtual method. But beware: Java has always shied away from using the term virtual methods and you will not find a mention of this term in Java’s vocabulary. This term is used in other OO languages like C and C++. Virtual method invocation is the invocation of the correct overridden method, which is based on the type of the object referred to by an object reference and not by the object reference itself. It’s determined at runtime, not at compilation time.
The OCP Java SE 7 Programmer II exam will question you on the need for overridden methods; the correct syntax of overridden methods; the differences between overloaded, overridden, and hidden methods; common mistakes while overriding methods; and virtual method invocation. Let’s get started with the need for overridden methods.
Note: A base class method is referred to as the overridden method and the derived class method is referred to as the overriding method.
Need Of Overridding Methods
In the same way we inherit our parents’ behaviors but redefine some of the inherited behavior to suit our own needs, a derived class can inherit the behavior and properties of its base class but still be different in its own manner — by defining new variables and methods. A derived class can also choose to define a different course of action for its base class method by overriding it. Here’s an example of class Book, which defines a method issueBook() that accepts days as a method parameter:
class Book {
void issueBook(int days) {
if (days > 0)
System.out.println("Book issued");
else
System.out.println("Cannot issue for 0 or less days");
}
}
Following is another class, CourseBook, which inherits class Book. This class needs to override the issueBook() method because a CourseBook can’t be issued if it’s only for reference. Also, a CourseBook can’t be issued for more than 14 days. Let’s see how this is accomplished by overriding the issueBook() method:
class CourseBook extends Book {
boolean onlyForReference;
CourseBook(boolean val) {
onlyForReference = val;
}
@Override #1
void issueBook(int days) { #2
if (onlyForReference)
System.out.println("Reference book");
else
if (days < 14)
super.issueBook(days); #3
else
System.out.println("days >= 14");
}
}
#1 Annotation—@Override
#2 Overrides issueBook() in base class Book
#3 Calls issueBook() defined in Book
The code at (#1) uses the annotation @Override, which notifies the compiler that this method overrides a base class method. Though optional, this annotation can come in very handy if you try to override a method incorrectly. (#2) defines the issueBook() method with the same name and method parameters as defined in class Book. (#3) calls the issueBook() method defined in class Book, however, it isn’t mandatory to do so. It depends on whether the derived class wants to execute the same code as defined by the base class.
Note: Whenever you intend to override methods in a derived class, use the annotation @Override. It will warn you if a method can’t be overridden or if you’re actually overloading a method rather than overriding it.
The following example can be used to test the preceding code:
class BookExample {
public static void main(String[] args) {
Book b = new CourseBook(true);
b.issueBook(100); #A
b = new CourseBook(false);
b.issueBook(100); #B
b = new Book(); #C
b.issueBook(100); #D
}
}
#A Prints “Reference book”
#B Prints “days >= 14”
#C b now refers to a Book instance
#D Prints “Book issued”
Figure 1 represents the compilation and execution process of class BookExample, as Step 1 and Step 2:
Step 1: The compile time uses the reference type for the method check.
Step 2: The runtime uses the instance type for the method invocation.
Figure 1 To compile b.issueBook(), the compiler refers only to the definition of class Book. To execute b.issueBook(), the Java Runtime Environment (JRE) uses the actual method implementation of issueBook() from class CourseBook.
Now let’s cover how to correctly override a base class method in a derived class.
Correct Syntax Of Overriding Methods
Let’s start with an example of the overridden method review, as follows:
class Book {
synchronized protected List review(int id,
List names) throws Exception { #A
return null;
}
}
class CourseBook extends Book { #B
@Override
final public ArrayList review(int id,
List names) throws IOException { #C
return null;
}
}
#A Method review in base class Book
#B CourseBook extends Book
#C Overridden method review in derived class CourseBook
Figure 2 shows the components of a method declaration: access modifiers, nonaccess modifiers, return type, method name, parameter list, and a list of exceptions that can be thrown (method declaration isn’t the same as method signature). The figure also compares the review method defined in base class Book, with the overridden method review() defined in class CourseBook with respect to these identified parts.
Figure 2 Comparing parts of a method declaration for a base class method and overridden method.
Table 1 compares the method components shown in figure 2.
Table 1: Comparison of method components and their acceptable values for an overriding method
Method component | Value in class Book | Value in class CourseBook | Overriding method review() in class CourseBook |
Access modifier | protected | public | Define same access or less restrictive access than the review() method in the base class. |
Nonaccess modifier | synchronized | final | Overridding method can use any nonaccess modifier for an overridden method. A nonabstract method can also be overridden to an abstract method. But a final method in the base class cannot be overridden. A static method cannot be overridden to be nonstatic. |
Return type | List | ArrayList | Define the same or a subtype of the return type used in the base class method (covariant return types). |
Method name | review | review | Exact match. |
Parameter list | (int id, List names) | (int id, List names) | Exact match. |
Exceptions thrown | throws Exception | throws IOException | Throw none, same or a subclass of the exception thrown by the base class method. |
Exam Tip: The rule listed in table 1 on exceptions in overridding methods only applies to checked exceptions. An overridding method can throw any unchecked exception (RuntimeException or Error) even if the overridden method doesn’t. The unchecked exceptions aren’t part of the method signature and aren’t checked by the compiler.
Chapter 6 includes a detailed explanation on overridden and overriding methods that throw exceptions. Let’s walk through a couple of invalid combinations that are important and very likely to be on the exam.
Note: Though a best practice, I’ve deliberately not preceded the definition of the overridden methods with the annotation @Override because you might not see it on the exam.
Access Modifiers
A derived class can assign the same or more access but not a weaker access to the overriding method in the derived class:
class Book {
protected void review(int id, List names) {}
}
class CourseBook extends Book {
void review(int id, List names) {} #A
}
#A Won’t compile; overriding methods in derived classes can’t use a weaker access
Nonaccess Modifiers
A derived class can’t override a base class method marked final:
class Book {
final void review(int id, List names) {}
}
class CourseBook extends Book {
void review(int id, List names) {} #A
}
#A Won’t compile; final methods can’t be overridden
Argument List and Covariant Return Types
When the overriding method returns a subclass of the return type of the overridden method, it’s known as a covariant return type. To override a method, the parameter list of the methods in the base and derived classes must be exactly same. It you try to use covariant types in the argument list, you’ll end up overloading the methods and not overriding them. For example:
class Book {
void review(int id, List names) throws Exception { #1
System.out.println("Base:review");
}
}
class CourseBook extends Book {
void review(int id, ArrayList names) throws IOException { #A
System.out.println("Derived:review");
}
}
#1 Argument list—int and List
#A Argument list—int and ArrayList
At (#1) the review() method in base class Book accepts an object of type List. The review() method in derived class CourseBook accepts a subtype ArrayList (ArrayList implements List). These methods aren’t overridden—they’re overloaded:
class Verify {
public static void main(String[] args)throws Exception {
Book book = new CourseBook(); #1
book.review(1, null); #A
}
}
#1 Reference variable of type Book used to refer to object of CourseBook
#A Calls review in Book; prints “Base:review”
The code at (#1) uses a reference variable of type Book to refer to an object of type CourseBook. The compilation process assigns execution of the review()method from base class Book to the reference variable book. Because the review method in class CourseBook doesn’t override the review method in class Book, the JRE doesn’t has any confusion regarding whether to call the review() method from class Book or from class CourseBook. It moves forward with calling review() from Book.
Exam Tip: It’s the reference variable type that dictates which overloaded method will be chosen. This choice is made at compilation time.
Exceptions Thrown
An overriding method must either declare to throw no exception, the same exception, or a subtype of the exception declared to be thrown by the base class method, or else it will fail to compile. This rule, however, doesn’t apply to error classes or runtime exceptions. For example:
class Book {
void review() throws Exception {}
void read() throws Exception {}
void close() throws Exception {}
void write() throws NullPointerException {}
void skip() throws IOException {}
void modify() {}
}
class CourseBook extends Book {
void review() {} #A
void read() throws IOException {} #B
void close() throws Error {} #C
void write() throws RuntimeException {} #D
void skip() throws Exception {} #E
void modify() throws IOException {} #F
}
#A Compiles; declares to throw no exception
#B Compiles; declares to throw IOException, a subclass of Exception
#C Compiles; an overriding method can declare to throw any Error
#D Compiles; an overriding method can declare to throw any RuntimeException
#E Doesn’t compile; declares to throw Exception, a superclass of IOException. Overriding method can’t declare to throw broader exceptions than declared to be thrown by overridden method.
#F Doesn’t compile; declares to throw IOException. Overriding method can’t declare to throw a checked exception if overridden method doesn’t.
Exam Tip: An overriding method can declare to throw any RuntimeException or Error, even if the overridden method doesn’t.
To remember this preceding point, let’s compare exceptions with monsters. Figure 3 shows a fun way to remember the exceptions (monsters) that can be on the list of an overriding method, when the overridden method doesn’t declare to throw a checked exception and when it declares to throw a checked exception.
Figure 3 Comparing exceptions to monsters. When an overridden method declares to throw a checked exception (monster), the overriding method can declare to throw none, the same, or a narrower checked exception. An overriding method can declare to throw any Error or RuntimeException.
Can You Override All Methods From the Base Class or Invoke Them Virtually?
The simple answer is no. You can override only the following methods from the base class:
Methods accessible to a derived class
Nonstatic base class methods
Methods Accessible to a Base Class
The accessibility of a method in a derived class depends on its access modifier. For example, a private method defined in a base class isn’t available to any of its derived classes. Also, a method with default access in a base class isn’t available to a derived class in another package. A class can’t override the methods that it can’t access.
Only Nonstatic Methods Can Be Overridden
If a derived class defines a static method with the same name and signature as the one defined in its base class, it hides its base class method and doesn’t override it. You can’t override static methods. For example:
class Book {
static void printName() { #A
System.out.println("Book"); #A
} #A
}
class CourseBook extends Book {
static void printName() { #B
System.out.println("CourseBook"); #B
} #B
}
#A Static method in base class
#B Static method in derived class
The printName() method in class CourseBook hides printName() in class Book. It doesn’t override it. Because the static methods are bound at compile time, the printName() method that’s called depends on the type of the reference variable:
class BookExampleStaticMethod {
public static void main(String[] args) {
Book base = new Book();
base.printName(); #A
Book derived = new CourseBook();
derived.printName(); #B
}
}
#A Prints “Book”
#B Prints “Book”
Identifying Method Overriding, Overloading, and Hiding
It’s easy to get confused with method overriding, overloading, and hiding. Figure 4 identifies these methods in classes Book and CourseBook. On the left are the class definitions, and on the right their UML representations.
Figure 4Identifying method overriding, method overloading, and method hiding in a base and derived class.
Exam Tip: When a class extends another class, it can overload, override, or hide its base class methods. A class can’t override or hide its own methods—it can only overload its own methods.
Let’s check out the correct code for defining a static or nonstatic method in a derived class that overrides or hides a static or nonstatic method in a base class using the next “Twist in the Tale” exercise (answer to this exercise is included at the end of this article).
A Quick Note on the "Twist in the Tale" Exercise
Each chapter (from which this article is exceprted) includes a few Twist in the Tale exercises. For these exercises, I’ve tried to use modified code from the examples already covered in a chapter, and the "Twist in the Tale" title refers to modified or tweaked code. These exercises highlight how even small code modifications can change the behavior of your code. They should encourage you to carefully examine all of the code on the exam.
My main reason for including these exercises is that on the real exam you may be asked to answer more than one question that seems exactly the same as another. But upon closer inspection, you’ll realize that these questions differ slightly, and that these differences change the behavior of the code and the correct answer option.
Twist in the Tale
Let’s modify the code of classes Book and CourseBook and define multiple combinations of the static and nonstatic method print() in both these classes as follows:
(a)
class Book{
static void print(){}
}
class CourseBook extends Book{
static void print(){}
}
(b)
class Book{
static void print(){}
}
class CourseBook extends Book{
void print(){}
}
(c)
class Book{
void print(){}
}
class CourseBook extends Book{
static void print(){}
}
(d)
class Book{
void print(){}
}
class CourseBook extends Book{
void print(){}
}
Your task is to first tag them with one of the options and then compile them on your system to see if they’re correct. On the actual exam, you’ll need to verify (without a compiler) if a code snippet compiles or not:
Overridden print method
Hidden print method
Compilation error
Can You Override Base Class Constructors or Invoke Them Virtually?
The simple answer is no. Constructors aren’t inherited by a derived class. Because only inherited methods can be overridden, constructors cannot be overridden by a derived class. If you attempt an exam question that queries you on overriding a base class constructor, you know that it’s trying to trick you.
Exam Tip: Constructors can’t be overridden because a base class constructor isn’t inherited by a derived class.
Answer to Twist in the Tale
Purpose: To distinguish between overloaded, overridden and hidden methods.
Answer and explanation: Combination number (a) compiles successfully. The static method print() in CourseBook hides the static method print() in its base class, Book:
class Book {
static void print() {}
}
class CourseBook extends Book {
static void print() {}
}
Instance methods can override methods from their base class, but static methods don’t. When a derived class defines a static method with the same signature as one of the methods in its base class, it hides it. Static methods don’t participate in polymorphism.
Combination number (b) won’t compile. The static method print() in the base class Book can’t be hidden by instance method print() in the derived class CourseBook:
class Book {
static void print() {}
}
class CourseBook extends Book {
void print() {}
}
Combination number (c) won’t compile either. The instance method print() in base class Book can’t be overridden by the static method print() in derived class CourseBook:
class Book{
void print() {}
}
class CourseBook extends Book {
static void print() {}
}
Combination (d) compiles successfully. The instance method print() in CourseBook overrides the instance method print() in its base class, Book:
class Book{
void print() {}
}
class CourseBook extends Book {
void print() {}
}
I wish Good Luck to you and hope you earn this certification with flying colors!
With much respect,
Mala
Opinions expressed by DZone contributors are their own.
Comments