java Inheritance Tutorial

Welcome to the seventh lesson ‘Inheritance’ of the Core Java Tutorial, which is a part of the Java Certification Training Course. In this lesson, we will talk about Inheritance, which is a core pillar of the object-oriented programming systems.

Objectives

At the end of this lesson on Inheritance, you should be able to:

  • Define Inheritance

  • Understand the use of Polymorphism

  • Determine when casting is necessary

  • Discuss “super” and “this” keywords

  • Use abstract classes and interfaces

What Is Inheritance?

Inheritance is a mechanism by which one class acquires all the properties and behaviors of the parent class.

It can be used for method overriding and code reusability.

Inheritance, in the true sense, is very similar to what we face in real life, We inherit whatever our ancestors have and that's the same concept. So rather than reinventing the wheel, inheritance helps us to reduce costs and effort of testing in application development.

It is because when you create a class with implementation, you wouldn't want to make major changes to that class when your customers come to you for change requests in the court because you may break something that's already working.

And there is a cost of retesting the entire class. So it's much better to use an extended class, or a subclass, or a child class which inherits from the base class. We can provide that extended feature or new functionality or change request in the inherited class.

The syntax of Java Inheritance is as shown:

class Subclass-name extends Superclass-name
{
//methods and fields
}

We have the name of the class, the extents keyword which provides inheritance in Java, and the name of the superclass. So the child class is called the subclass and the base class is called a superclass.

Using inheritance feature in Java, you can create new classes that are built by reusing code already existing in the base classes.

Polymorphism

Polymorphism is a feature of an object that takes on different forms depending on the object on

top of it.

It is of two types:

  • Method Overriding

  • Method Overloading

Let's take an example:

public interface Veg{ }

public class Animal{ }

public class Cow extends Animal implements Veg{ }

In the above example, Animal is the base class and cow is the child class. So cow is the specialization while Animal is a generalization.

So we can say that the cow class is polymorphic in nature which means it takes on multiple forms. For example, the cow is also a type of animal since it extends from the base class animal and the cow is also vegetarian in its behavior since it implements the vegetarian interface.

Method Overriding

You can produce a new class based on an old one and also modify the existing behavior of the parent class.

If a new method is defined in a subclass with name, return type, and argument list that matches the method in the parent class, then the method is said to overriding the old method.

class House

{

void shelter(){

System.out.println(“We live in rooms");

}

}

class Villa extends House {

public static void main(String args[ ]) {

Villa obj = new Villa();

obj.shelter();

}

}

In the above example, the base class is class house. It has a method called void shelter with a simple print message ‘we live in rooms.’

Here we have an extended class called villa that extends the House class. This has the entry point of our program and then we are creating an object of this class.

Then we have obj.shelter() command, which executes the shelter method that is inherited from the base class house.

Method Overloading

Method overloading is a feature that allows a class to have two or more methods that have the

same name but different arguments.

So these three methods are expected to be in the same class in method overriding. The methods are expected to be in the child and the base class. For example, we create a class called taxcalculator and have a method in it called calculatetax().

Now we are writing the implementation of calculatetax() inside the base class since we have complete clarity over what that method should be doing.

But post the budget, let's say the government decides of different rates for GST etc., for tax calculation. Rather than modifying the base method, we could have an extended calculator class called GST which extends from the base class.

The extended class then provides a method called calculatetax(). So we don't change the name of the method. Now we have a method with the same name in the base and the child class but with different implementations.

So, that is an example of method overriding.

In method overloading, you have exactly the same method with the same name in the same class but with different signatures. By signatures, we mean that the argument list can be different. So either the number of parameters that you passed to each function can be different, or the data types of those parameters can be different, or the sequence of those data types or parameters passed can be different.

Consider an example shown below:

public void add(int a,int b) { }

public void add(int a,int b, int c) { }

public void add(float a,float b) { }

As can be seen, all the three methods are ‘add’.

In the first method, you can see it takes two integers, the second method takes three integers, while the third method takes two floats.

Now, method overloading helps in encapsulation because it maintains the same interface name. Clients, or other classes that are using your API or making use of the class objects you've created, will always call your method named add.

Based on the parameters that they pass, they would decide which add method gets called.

Now, this helps when you already have declared an add method and there are some existing APIs or classes that are using your add method.

Going forward, to make it simple, you do not want to give added function names so you can create another add method with different parameters. This can be done so that the newer clients, who are connecting to your code base, can call your new function whereas older clients can continue to call the older add method.

So the name of the interface does not change. All clients continue to call add methods, but depending on the number of parameters or the data types they pass, they can decide whether they call the newer add method or the older one.

Difference Between Object Type and Reference Type

Object type is the type of object that we create. The reference type is used to refer to an object.

Let us understand them with an example:

class Animal {

}

class Lion extends Animal {

}

public class Test {

public static void main() {

Animal l = new Lion(); // reference type is Animal and object is Lion

}

}

In the above example, we have a class Animal, which is a base class. Then we have class Lion, which is a child class that inherits and extends from the Animal class. In the main, we create an object of the animal class and making it point to the child class lion. So this is a reference type.

Here, ‘l’ is an object of the base class, which is pointing to a type of lion. So here you see that ‘Animal l’ is actually a reference type, and the object is Lion because this particular class object is referencing an object of type Lion.

Reference Data Types

Reference data types are created using defined constructors of classes. They are declared to be of a specific type that cannot be changed. They are used to access objects.

They have a default value of null.

So if we just declare animal as an object, it'll be null, unless it points to the animal or to the specialized class of Lion.

Example:

Animal animal = new Animal("giraffe");

instanceof Operator

When you pass objects using references to their parent class and you want to know what

objects you have, you can use instanceof operator.

Let us now understand instanceof operator using an example:

public class Employee extends Object

public class Manager extends Employee

public class Engineer extends Employee

Here, we have a class Employee, which extends from the base java type object. Then we have class Manager, which extends from employees. Then we have engineer class which further goes and extends from the Manager.

So this is a multi-level inheritance where manager is a type of employee. There are some properties which are specific to the Manager. Engineer is also the manager, so he has the same properties that are defined by the manager, but some additional responsibilities. And, of course, he is also an employee.

Now, given the context of that example, if you receive an object using a reference of type “Employee,” it might refer to a “manager” or an “engineer.”

You can test it using the instanceof operator as shown:

public void doSomething (Employee e)

{

if (e instanceof Manager) {

//Process a Manager

} else if (e instanceof Engineer) {

//Process a Engineer

}

else {

//Process any other type of Employee

}

So here is a function named “doSomething” and we use the instanceof operator to check if he is an instance of manager (process as per the manager functions) else if we can check if the object passed to this function is of base type Employee.

Now we need to check whether that is a manager object or an Engineer object. If it is a Manager object, instanceof will return true.

If it is an engineer object we can check with the instanceof operator to specify which type of object we're receiving and this is very important in our inheritance scenario.

Casting

Casting is used to get the full access to the object that has been determined using the instanceof operator.

For example, if you have received the information of a parent class and determined that the object is a specific subclass using instanceof operator and you require the full access to that object, you can use casting.

Consider the code given below:

public void doSomething (Employee e)
{
if (e instanceof Manager)
{
Manager m = (manager) e;
System.out.println(“This is the manager of ” + m.getDepartment());
}
//rest of operation
}

Here we're actually passing a reference of type employee. So the first line denotes a reference type which is not an object.
Now we need to check whether ‘e’ is holding a reference to the type manager or to type engineer. If the mentioned if condition returns true, (indicating that this reference type ‘e’ that was passed to this function is holding a reference of object type manager), we will need to explicitly typecast this particular object to manager, so that we can store it as a Manager object.

So we create the object of type Employee and make it point to manager. Now, this object, being a reference of type Employees, will only be able to access the Employees section of the data that is there in the memory for this object.

The moment you make it a point to manager, there is also an additional aspect or additional data and properties that are associated with this reference object.

But since it is essentially a reference of the type Employee or the base type, it will not be able to access the content that is specific to manager.

It will only access the content that is specific to the Employee within the object. Once you do a typecast and forcibly say that this object has to be typecast as manager, that is when it will get access to the properties that are specific to the Manager.

And this is where typecasting is very essential in inheritance because this employee object is essentially a reference of type Employee. To extract the manager aspect or data out of it, we do an explicit typecast of type Manager and then store it in a manager object.

What happens if we don’t perform casting?

If you don't perform casting and attempt to execute any method on the manager, it will simply give you an error.

super and this Keywords

‘super’ and ‘this’ keywords are also very essential when working with inheritance. The super keyword is a reference variable used to refer immediate parent class object.

For example, the Employee class is the base class and the Manager class is inheriting from it. Let's say, here we have a variable called x.

To access this x variable, we can simply say:

super.x

and the super keyword gives us the access to the context that is defined in the parent class.

It can be used to refer to the immediate parent class instance variables. It can also be used to invoke immediate parent class methods and constructors.

Let's take an example of the super keyword. The code is as shown below:

class Animal
{
String color=“black";
}
class Cat extends Animal{
String color=“white";
void printColor(){
System.out.println(color);                                   //prints color of Cat class
System.out.println(super.color);                     //prints color of Animal class
}
}
class Super1{
public static void main(String args[]){
Cat c=new Cat();
c.printColor();
}}

Here we have a class Animal with a string variable ‘color.’

Class Cat is the specialized class and a child class that extends from Animal. And this class has one additional parameter which says:

String color = “white”;

In the print color method, we're saying that we want to print color. Now if you observe, both these classes have data members with the same name which is conflicting.

So when we say:

System.out.println(color);

This actually prints the color defined in Cat class.

But if we say:

System.out.println(super.color);

It actually prints the color defined in the base class Animal.

So now we just create an object of the Cat class and we simply go and print the color.

We would get white and black.

this Keyword

this keyword is a reference variable that refers to the current object that is calling that particular method. It can be used to refer to the current class instance variable. It can also be used to invoke the current class methods and current class constructor.

Consider the code given below:

class A2 {
void m() {
System.out.println(this);                         //prints same reference ID
}
public static void main(String args[])  {
A2 obj=new A2();
System.out.println(obj); //prints the reference ID
obj.m();
}
}

Here we have a class A2 that has a method m() which simply says

system.out.println(this);

It prints the ‘this’ pointer. this is a pointer that points to the instance that is calling this method m().

In the main function, we're creating an object of the A2 class and we're simply trying to print that object which gives you the reference id of the object.

Now we call obj.m()

It's the same object used to call the m() method which means that this pointer will actually point to the object that is calling this method which is nothing but obj.

Hence the same reference ID will be printed out.

Let's look at an example using super and this keyword.

class Person
{
String name;
Person(String name)   {
this.name=name;
}
}
class Emp extends Person  {
float salary;
Emp(String name,float salary) {
super(name);             //reusing parent constructor
this.salary=salary;
}
void display(){System.out.println(name+" "+salary);  }
}
class Super{
public static void main(String[] args) {
Emp e1=new Emp(“Vikram",45000f);
e1.display();
}}

We have a class Person with a string variable name. Next, we have a constructor (with the same name as that of the class) which takes a parameter and it assigns that to the data member or the class variable.

Next, we have class Emp that extends from Person class and a float variable salary.

Now here we have a constructor of this class with the same name as that of the class name. It takes two parameters, name and salary, and it passes the name parameter to the base class.

So the moment we say

super(name);

it will call the base class constructor and assign this value.

This happens because we create an object of Emp class and pass name and salary, but the name does not actually belong to the Emp class but to the Person object.

Due to inheritance, it is also available within the Person i.e. within the Emp object.

But when we create an object of Emp class and pass the name, the name and salary both are passed to this constructor.

Further, the name, using the super keyword, is sent to the base class constructor. Here you see the benefit of actually using the super keyword.

And the command:

this.salary = salary;

is a best practice while coding.

this.salary refers to this local salary variable, so that prevents you from creating errors and mixing up parameters with class member variables that have been defined.

Abstract Class and Interfaces

An abstract class is actually declared because it cannot be instantiated. It's just a placeholder class which defines the core business logic that is captured at the initial time of system design.

We generally declare an abstract class when, at the start of the system design, we do not have clarity on the various class implementations or the various methods that need to be implemented within our application.

This is the reason we would need to create an extended class. An abstract class generally will always be created as a base class. An abstract class does not define concrete methods. It only defines abstract methods which can be overwritten in the child classes.

Typically an abstract class never be instantiated because it always has to be extended.

Let's take an example.

Let's say we are building an application to automate e-retailer or electronic store. Now the electronics store can sell multiple products. During the initial system design, we're never sure as to what product the store can sell.

So you would model the class or system as a generic product class. Now we definitely cannot walk over to a store and expect them to create an object of a generic product. We would always expect them to create an object of a specialized class.

Hence the moment we try to create an object of a product class, we would be given an error because it maps to real life. We cannot walk into a store and ask somebody to generate a bill or an invoice for a generic product.

That is the reason, the moment we have clarity down the line that the store is going to sell a DVD player, we would have class DVD, inherit from the base product, and then we would create an object of the DVD class.

So the base class is a skeletal class. It's just a placeholder class or a foundation class. So an abstract class is like a foundation in the construction industry. We can't create an object or actually live in it, but it plays a crucial role because it holds the entire class hierarchy together and plays the role of an anchor class.

Abstraction is another feature available in the OOPS programming system and is a pillar of OOPS. It is the process of hiding and implementing details and showing only functionality to the user.

For example, if we go to an ATM, we can withdraw cash but we will not be able to see how the withdrawal is happening or what are the steps required to withdraw cash.

So the implementation of how the withdrawal happens is hidden. What we can see is only the interface in terms of what are the options that the atm provides.

So the Java programming language enables a class designer to specify that a superclass declares a method that does not supply an implementation. Such a method is called is an abstract method.

Now, what is an abstract method?

We discussed the product class that we're going to create for this shopping store. Now if you look at the product class, you can actually see that the product class will not have a definite method defined inside it.

Let's take an example of a method which says Generate Invoice. Now we can't walk into a store and asked the executive to generate an invoice for the generic product, which means a generic product would not have a price.

Only when we have clarity, that the store is going to sell a DVD player, we can write the method inside the DVD class which says Generate Invoice. The method then multiplies price and the quantity and generates the bill.

So when you are declaring or creating the abstract class, since it's a placeholder class, it only has a method declaration without a body. So the abstract class only defines the business rules that govern the execution of the application.

So the business rule, in this case, would be to generate an invoice, but how the invoice is going to get generated is something that can be defined only in the child class DVD. It’s because only then we will have the clarity of the price and the quantity of the products that the user has ordered.

So abstraction is a process of hiding data only showing what capabilities your class has and not how it implements it.

A sample program depicting the use of abstract class is as shown below.

abstract class Shape
{
abstract void draw();
}
class Triangle extends Shape{
void draw(){System.out.println("drawing triangle");}
}
class Rectangle extends Shape{
void draw(){System.out.println("drawing Rectangle");} //In real scenario, a method is called by programmer or user
}
class TestAbstraction1{
public static void main(String args[]){
Shape s = new Rectangle(); //In real scenario, an object is provided through method; e.g., getShape() method
s.draw();
}
}

So when we look at an abstract class, it has an abstract method which is only having a declaration. So you only know what that particular product class should do.

We know that it should generate an invoice. But we do not know how it should generate an invoice, because we do not even know that the store may even go and sell a DVD.

Interfaces

An interface is a blueprint of a class that has static constants and abstract methods. All the methods are abstract by default. It is a mechanism to achieve abstraction and multiple inheritance.

Consider the image shown below:

If you look here, a class can implement more than one interface and an interface also can implement other interfaces.

Now let's take an example of the electronic store that we were building. So now the electronic store came to us and said that they needed a software solution. As developers, we started building the software solution and we created the product class because we knew that they were going to sell products.

But what we did not know was what type of products are they going to sell. So the store told us that they needed an application which was scalable and would work not only today but five years down the line as well.

So today we will be able to generate products but later on, we should also be able to generate DVDs, mp3 players, TVs, etc. That's the reason we took a design decision that we're going to create an abstract class called Product. Whatever we declare inside that class is going to be just generic business implementations without a body.

Once the application is live, the shopping store management reported that the software did not factor in tax computation on billing or generating invoices. So there is a new government rule which says that a certain product should be taxed.

Now, how do we implement it?

We cannot go and put a new abstract method in the base class (being a placeholder class). It will break the entire class hierarchy.

So for this extended business rule of tax computation that came much later in the development cycle, we incorporated into an interface. Whichever class, like DVD or TV, needs to pay taxes, that class will go and implement that interface.

Hence it will get that extended business rule and we can overwrite that method.

So interfaces only have abstract business rules. They do not have methods with the body because they just define a business rule as a contract that certain classes need to go and implement, override and provide their own implementation for that business rules.

The Relationship between Class and Interface

So a class can extend another class, and an interface can also extend an interface. A class can implement an interface.

Consider a program shown below:

interface sample
{
void print();
}
class A implements sample{
public void print(){System.out.println(“Interface example");}
public static void main(String args[]){
A obj = new A();
obj.print();
}
}

Interfaces in Java 8 - Static Method

Static methods is an ability to define concrete (static and defaults) methods and interface. It is a new concept of default method implementation in interfaces, which is added for backward compatibility so that the old interfaces can be used to leverage the lambda expression capability of Java 8.

So the static method looks like a normal class as shown.

public interface Test {
...
public static boolean isNull(Object obj) {
return obj == null;
}
...

Inside the interface, we have static (constant) method that is allowed. And the reason to add static methods to the interface is is to keep related utility methods in one place so that they can be used easily by subclasses, default methods and subinterfaces, or by users of their interfaces.

Interfaces in Java 8 - Default Method

Now the default method looks like a typical class method but is defined inside an interface and contains the default specifier. So here is an example of a default method.

The default method does a simple iteration over the set of values and the default keyword is what allows you to create a default method in an interface.

default boolean removeIf(Predicate<? super E> filter)
{
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}

Default methods can access everything that is defined within the interface or is inherited by the interface, including:

  • reference to this pointer

  • all abstract methods defined in this or super or the base interfaces

  • all default or static methods defined in this or super interfaces

  • all static fields defined in this or super interfaces

Summary

The key takeaways from this lesson are:

  • Inheritance is a mechanism by which one object acquires all the properties and behaviors of the parent object.

  • Polymorphism is a feature of an object that takes on different forms depending on the object on top of it.

  • Casting is used to get the full access to the object that has been determined using the instanceof operator.

  • The super keyword is a reference variable used to refer immediate parent class object, and this is a reference variable that refers to the current object.

  • Any class with one or more abstract methods is called Abstract class, and interface in java is a mechanism to achieve abstraction.

Conclusion

With this, we come to an end of this lesson on Inheritance. The next lesson focuses on Exception Handling.

Find our Java Certification Training Online Classroom training classes in top cities:


Name Date Place
Java Certification Training 12 Oct -24 Nov 2019, Weekend batch Your City View Details
  • Disclaimer
  • PMP, PMI, PMBOK, CAPM, PgMP, PfMP, ACP, PBA, RMP, SP, and OPM3 are registered marks of the Project Management Institute, Inc.

Request more information

For individuals
For business
Name*
Email*
Phone Number*
Your Message (Optional)
We are looking into your query.
Our consultants will get in touch with you soon.

A Simplilearn representative will get back to you in one business day.

First Name*
Last Name*
Email*
Phone Number*
Company*
Job Title*