In this post, you will learn to:
- Explain the concept of inheritance.
- State the purpose of method overriding.
- State the use of a super keyword.
- Explain covariant return types.
Inheritance in Real World
In a family, the traits of parents are often inherited by their children. In addition to having unique traits and behavior of their own, children inherit those of their parents. This is evident also among other entities. For example, a sports car, a luxury car, and a family car are different types of cars.
Each has unique characteristics and behavior, but since they belong to a common category – car, they have certain common features, such as wheels, brakes, gearbox, and movement. In other words, these different categories of cars have inherited common features from the common category, car.
Inheritance in Programming
Inheritance is an important and integral part of object-oriented programming. Inheritance enables you to define a very general class first, and then define more specialized classes later by simply adding some new details to the general class definition. This saves work and encourages code reuse and customization, because the specialized class inherits all the properties and methods of the general class, only new properties and methods need to be implemented. In other words, a user can simply derive the new class from the existing class without having to write or debug the new code.
Inheritance plays a vital role in Java programming and it is imperative to understand following details and how it works.
Subclassing
The extends keyword is used to create a subclass. Some rules about subclassing are as follows:
- A class can be directly derived from only one class.
- If a class does not have any superclass, then it is implicitly derived from Object class which is the parent of all Java classes.
- A subclass can inherit all the protected members of its superclass. The protected keyword is an access specifier which, when used with a member in the superclass, allows that member to be used by other classes derived from the superclass.
- Unlike other members, constructors cannot be inherited.
The following is the syntax to create a subclass in Java.
public class <class_name2> extends <class_name1> { ... ... }
where,
<class_name2> is the name of the subclass
<class_name1> is the name of the superclass
The following code demonstrates the declaration of a superclass and a subclass inherited from it.
class Car { public int mileage; public String color; protected String make; public void accelerate(){ System.out.println("Car is Accelerating"); } } public class LuxuryCar extends Car{ // Luxury defines an additional feature // named perks public String perks; public static void main(String[] args) { Car objCar = new Car(); LuxuryCar objLuxuryCar= new LuxuryCar(); objCar.accelerate(); System.out.println("Now inside LuxuryCar"); // This will call the inherited // method accelerate objLuxuryCar.accelerate(); } }
Here, Car is a class having members, such as mileage, color, and make. It has one method named accelerate(). The class, LuxuryCar is derived from the class Car using the keyword extends, and hence becomes a subclass of the Car class. The class, LuxuryCar inherits all public and protected members of the Car class. It also defines a new member named perks. Though the LuxuryCar class has not defined a method named accelerate(), the inherited method is executed, when a call to it is made through the object, objLuxuryCar.
Output:
Car is Accelerating Now inside LuxuryCar Car is Accelerating
Overriding
When a subclass defines a new method having the same signature as the superclass method, then the process is called overriding. The purpose of overriding is to define new or different behavior for the objects of the subclass.
For instance, taking a real-world example, a 1970’s model of a Toyota car also had a basic action of driving and the latest model of a Toyota car also has the same basic action but with greater and better performance. So, though the basic action still remains the same, how the models implement the action is different in each case.
Method Overriding Rules
Some rules that you should remember when overriding methods are as follows:
- The overriding method must match in name, type, number of arguments and return type with the overridden method.
- An overridden method cannot have a weaker access specifier than the access specifier of the method it overrides. For example, a protected method in a superclass can be overridden by a public method having same signature, but a public method in a superclass cannot be overridden by a protected method in the subclass.
‘super’ Keyword
Typically, when a subclass overrides a superclass method and invokes it later, it is the subclass’s method that will be called. Sometimes, it is required to call the overridden method in the superclass from within a subclass. This can be performed by using the keyword super. The super keyword can also be used to access the instance variables of the superclass.
The basic purpose of super keyword is to enable access to those members of the superclass that are presently hidden by members in the subclass. The following code demonstrates the use of super.
class Car { public void accelerate() { System.out.println("Car is accelerating"); } } public class LuxuryCar extends Car { // Overriding the method accelerate public void accelerate() { // Calling accelerate of superclass super.accelerate(); System.out.println("Now inside LuxuryCar"); System.out.println("Luxury Car is Accelerating"); } public static void main(String[] args) { LuxuryCar objLuxuryCar = new LuxuryCar(); objLuxuryCar.accelerate(); } }
In this example, the class Car has a method accelerate(). Another class, the LuxuryCar is derived from the class, Car. It defines its own version of accelerate(), thus overriding the inherited method. Within this overridden method in the class LuxuryCar, it invokes the accelerate() method of the class Car using the super keyword. In the main method, an object is created only for the class, LuxuryCar. The accelerate() method is then invoked through this object.
Output:
Car is Accelerating Now inside LuxuryCar Luxury Car is Accelerating
‘super’ Keyword with Constructors
Unlike other members of a class, constructors are not inherited when a class is derived from another class. The super keyword is used to access the superclass’s constructor. It must be the first statement in the constructor of the subclass. The following is the syntax of super keyword used to access the superclass’s constructor.
super([parameters]);
The following code demonstrates the use of super keyword to access superclass’s constructor.
class Car { protected String make; public Car() { System.out.println("Constructor of Car invoked"); } public void displayData() { System.out.println("Data of Car"); } } public class LuxuryCar extends Car { public String make; public LuxuryCar(){ // Accessing base class constructor super(); // Accessing base class instance variable super.make= "Car"; // accessing base class method super.displayData(); System.out.println("Constructor of Luxury Car"); } public static void main(String[] args) { LuxuryCar objLuxuryCar= new LuxuryCar(); } }
In the code, the class Car has a constructor. The class LuxuryCar is derived from the class Car. LuxuryCar also has a constructor and invokes the constructor of the superclass using the super keyword. In addition to this, instance variable and method of base class are accessed using the super keyword. In the main method, an object is created only for the class LuxuryCar. The accelerate() method is then invoked through this object.
Output:
Constructor of Car Data of Car Constructor of Luxury Car
Covariant Return Types
A new feature in the J2SE 5.0 allows an overriding method to return an object whose type is a subclass of the type returned by the overridden method in the superclass. This is called a covariant return type. The main advantage of a covariant return type is that it reduces type casting and type-checking to a great extent. For example, consider a superclass named Student which implements two methods named getMarks(). One method returns an instance of the java.lang.Number class and the other returns the java.lang.Integer class.
The following code demonstrates the implementation of a method with different return types.
class Student { public Number getMarks() { return new Number(); // Returns an object of the Number class. } public Integer getMarks() { return new Integer(); // Returns an object of the integer class. } }
An attempt to compile this class will result in the following error.
Student.java: 6: getMarks() is already defined in Student public Integer getMarks() { ^ 1 error
The reason for this error is that if a class were to call the getMarks() method on the Student class the compiler would not know which of the two methods it should invoke. Since, Integer is a sub-class of the Number class either version of the method could be correctly called. Now, consider that the superclass, Student, which has a single implementation of the getMarks() method. Also, consider that a subclass named ExchangeStudent overrides the same method getMarks(), which returns an object of type Integer which is a sub-type of the Number class.
The following code demonstrates overriding of superclass method in the subclass which returns a sub-type of the Number class.
class Student { public Number getMarks() { return new Number(); // Returns an object of the Number class. } } class ExchangeStudent { public Integer getMarks() { return new Integer(); // Returns an object of the integer class. } }
If this code is compiled in a JDK version earlier than the Java 2 Platform, Standard Edition (J2SE) 5.0 then a compile time error occurs. However, it compiles correctly under the J2SE 5.0 JDK.