Unit 3 Inheritance and Polymorphism
Unit 3 Inheritance and Polymorphism
Unit 3: Inheritance and Polymorphism: Inheritance in java, Super and sub class,
Overriding, Object class, Polymorphism, Dynamic binding, Generic programming, Casting
objects, Instance of operator, Abstract class, Interface in java, Package in java, UTIL
package.
Inheritance in Java
Example: In the following example, Animal is the base class and Dog, Cat and Cow are
derived classes that extend the Animal class.
Implementation:
// Parent class
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
// Child class
class Dog extends Animal {
void sound() {
System.out.println("Dog barks");
}
}
// Child class
class Cat extends Animal {
void sound() {
System.out.println("Cat meows");
}
}
// Child class
class Cow extends Animal {
void sound() {
System.out.println("Cow moos");
}
}
// Main class
public class Geeks {
public static void main(String[] args) {
Animal a;
a = new Dog();
a.sound();
a = new Cat();
a.sound();
a = new Cow();
a.sound();
}
}
Output
Dog barks
Cat meows
Cow moos
Explanation:
Syntax
class ChildClass extends ParentClass {
Note: In Java, inheritance is implemented using the extends keyword. The class that inherits
is called the subclass (child class) and the class being inherited from is called the superclass
(parent class).
Code Reusability: The code written in the Superclass is common to all subclasses.
Child classes can directly use the parent class code.
Abstraction: The concept of abstraction where we do not have to provide all details,
is achieved through inheritance. Abstraction only shows the functionality to the user.
Class: Class is a set of objects that share common characteristics/ behavior and
common properties/ attributes. Class is not a real-world entity. It is just a template
or blueprint or prototype from which objects are created.
Super Class/Parent Class: The class whose features are inherited is known as a
superclass(or a base class or a parent class).
Sub Class/Child Class: The class that inherits the other class is known as a
subclass(or a derived class, extended class or child class). The subclass can add its
own fields and methods in addition to the superclass fields and methods.
Note: The extends keyword establishes an "is-a" relationship between the child class and
the parent class. This allows a child class to have all the behavior of the parent class.
1. Single Inheritance
In single inheritance, a sub-class is derived from only one super class. It inherits the
properties and behavior of a single-parent class. Sometimes, it is also known as simple
inheritance.
Example:
//Super class
class Vehicle {
Vehicle() {
System.out.println("This is a Vehicle");
}
}
// Subclass
class Car extends Vehicle {
Car() {
System.out.println("This Vehicle is Car");
}
}
Output
This is a Vehicle
This Vehicle is Car
Prof T.I.Sarawad, Department of Computer Science Page 4
Unit 3: Inheritance and Polymorphism
2. Multilevel Inheritance
In Multilevel Inheritance, a derived class will be inheriting a base class and as well as the
derived class also acts as the base class for other classes.
Example:
class Vehicle {
Vehicle() {
System.out.println("This is a Vehicle");
}
}
class FourWheeler extends Vehicle {
FourWheeler() {
System.out.println("4 Wheeler Vehicles");
}
}
class Car extends FourWheeler {
Car() {
System.out.println("This 4 Wheeler Vehicle is a Car");
}
}
public class Geeks {
public static void main(String[] args) {
Car obj = new Car(); // Triggers all constructors in order
}
}
Output
This is a Vehicle
4 Wheeler Vehicles
This 4 Wheeler Vehicle is a Car
3. Hierarchical Inheritance
In hierarchical inheritance, more than one subclass is inherited from a single base class.
i.e. more than one derived class is created from a single base class. For example, cars and
buses both are vehicle
Example:
class Vehicle {
Vehicle() {
System.out.println("This is a Vehicle");
}
}
Output
This is a Vehicle
This Vehicle is Car
This is a Vehicle
This Vehicle is Bus
Note: that Java does not support multiple inheritances with classes. In Java, we can achieve
multiple inheritances only through Interfaces.
Example:
interface LandVehicle {
default void landInfo() {
System.out.println("This is a LandVehicle");
}
}
interface WaterVehicle {
default void waterInfo() {
System.out.println("This is a WaterVehicle");
}
}
// Subclass implementing both interfaces
class AmphibiousVehicle implements LandVehicle, WaterVehicle {
AmphibiousVehicle() {
System.out.println("This is an AmphibiousVehicle");
}
}
public class Test {
public static void main(String[] args) {
AmphibiousVehicle obj = new AmphibiousVehicle();
obj.waterInfo();
obj.landInfo();
}
}
Output
This is an AmphibiousVehicle
This is a WaterVehicle
This is a LandVehicle
5. Hybrid Inheritance
It is a mix of two or more of the above types of inheritance. In Java, we can achieve hybrid
inheritance only through Interfaces if we want to involve multiple inheritances to
implement Hybrid inheritance.
Explanation:
class Car extends Vehicle->Single Inheritance
class Bus extends Vehicle and class Bus extends Fare->Hybrid Inheritance (since Bus
inherits from two sources, forming a combination of single + multiple inheritance).
Code Reusability: Inheritance allows for code reuse and reduces the amount of
code that needs to be written. The subclass can reuse the properties and methods
of the superclass, reducing duplication of code.
Abstraction: Inheritance allows for the creation of abstract classes that define a
common interface for a group of related classes. This promotes abstraction and
encapsulation, making the code easier to maintain and extend.
Class Hierarchy: Inheritance allows for the creation of a class hierarchy, which can
be used to model real-world objects and their relationships.
Complexity: Inheritance can make the code more complex and harder to
understand. This is especially true if the inheritance hierarchy is deep or if multiple
inheritances is used.
Tight Coupling: Inheritance creates a tight coupling between the superclass and
subclass, making it difficult to make changes to the superclass without affecting the
subclass.
Polymorphism in Java
Multiple Behaviors: The same method can behave differently depending on the
object that calls this method.
Method Overriding: A child class can redefine a method of its parent class.
Method Overloading: We can define multiple methods with the same name but
different parameters.
Using polymorphism in Java has many benefits which are listed below:
Code Reusability: Polymorphism allows the same method or class to be used with
different types of objects, which makes the code more useable.
Abstraction: It allows the use of abstract classes or interfaces, enabling you to work
with general types (like a superclass or interface) instead of concrete types (like
specific subclasses), thus simplifying the interaction with objects.
Dynamic Behavior: With polymorphism, Java can select the appropriate method to call
at runtime, giving the program dynamic behavior based on the actual object type rather
than the reference type, which enhances flexibility.
1. Compile-Time Polymorphism
Compile-Time Polymorphism in Java is also known as static polymorphism and also known
as method overloading. This happens when multiple methods in the same class have the
same name but different parameters.
Method Overloading
As we discussed above, Method overloading in Java means when there are multiple
functions with the same name but different parameters then these functions are said to be
overloaded. Functions can be overloaded by changes in the number of arguments or/and a
change in the type of arguments.
// Class 1
// Helper class
class Helper {
// Method 2
// With same name but with 2 double parameters
static double Multiply(double a, double b)
{
// Returns product of double numbers
return a * b;
}
}
// Class 2
// Main class
class Geeks
{
// Main driver method
public static void main(String[] args) {
Output
8
34.65
Explanation: The Multiply method is overloaded with different parameter types. The
compiler picks the correct method during compile time based on the arguments.
2. Runtime Polymorphism
Method Overriding
Example: This program demonstrates method overriding in Java, where the Print()
method is redefined in the subclasses (subclass1 and subclass2) to provide specific
implementations.
// Java Program for Method Overriding
// Class 1
// Helper class
class Parent {
// Class 2
// Helper class
class subclass1 extends Parent {
// Method
void Print() {
System.out.println("subclass1");
}
}
// Class 3
// Helper class
class subclass2 extends Parent {
// Method
void Print() {
System.out.println("subclass2");
}
}
// Class 4
// Main class
class Geeks {
a = new subclass2();
a.Print();
}
}
Output
subclass1
subclass2
Explanation: In the above example, when an object of a child class is created, then the
method inside the child class is called. This is because the method in the parent class is
overridden by the child class. This method has more priority than the parent method inside
the child class. So, the body inside the child class is executed.
Advantages of Polymorphism
Encourages code reuse.
Simplifies maintenance.
Enables dynamic method dispatch.
Helps in writing generic code that works with many types.
Disadvantages of Polymorphism
It can make more difficult to understand the behavior of an object.
This may cause performance issues, as polymorphic behavior may require additional
computations at runtime.
Generic Programming
In Java, this is done using type parameters (like <T>, <E>, <K, V>, etc.).
Generics means parameterized types. They allows us to write code that works with
different data types using a single class, interface or method. Instead of creating separate
versions for each type, we use type parameters (like <T>) to make the code reusable and
type-safe.
Before Generics, Java collections like ArrayList or HashMap could store any type of
object, everything was treated as an Object. It had some problems.
If you added a String to a List, Java didn’t remember its type. You had to manually cast
it when retrieving. If the type was wrong, it caused a runtime error.
With Generics, you can specify the type the collection will hold like ArrayList<String>.
Now, Java knows what to expect and it checks at compile time, not at runtime.
1. Generic Class: A generic class is like a regular class but uses type parameters (like
<T>). It can accept one or more types, making the class reusable for different data types.
Such classes are called parameterized classes.
2. Generic Method: A generic method is a method that can work with different data types
using a type parameter. It lets you write one method that works for all types, instead of
repeating the same logic.
Generic Class
A generic class is a class that can operate on objects of different types using a type
parameter. Like C++, we use <> to specify parameter types in generic class creation. To
create objects of a generic class, we use the following syntax:
Note: In Parameter type, we can not use primitives like "int", "char" or "double". Use
wrapper classes like Integer, Character, etc.
Example:
T obj;
Test(T obj) {
this.obj = obj;
}
public T getObject() { return this.obj; }
}
class Geeks {
public static void main(String[] args)
{
// instance of Integer type
Test<Integer> iObj = new Test<Integer>(15);
System.out.println(iObj.getObject());
Output
15
GeeksForGeeks
{
System.out.println(obj1);
System.out.println(obj2);
}
}
class Geeks
{
public static void main (String[] args)
{
Test <String, Integer> obj =
new Test<String, Integer>("GfG", 15);
obj.print();
}
}
Output
GfG
15
Generic Method
We can also write generic methods that can be called with different types of arguments
based on the type of arguments passed to the generic method. The compiler handles each
method.
Example:
class Geeks {
Output
java.lang.Integer = 11
java.lang.String = GeeksForGeeks
java.lang.Double = 1.0
Limitations of Generics
The above line results in a compile-time error that can be resolved using type wrappers
to encapsulate a primitive type.
But primitive type arrays can be passed to the type parameter because arrays are
reference types.
ArrayList<int[]> a = new ArrayList<>();
Example:
class Test<T> {
// An object of type T is declared
T obj;
Test(T obj) { this.obj = obj; } // constructor
public T getObject() { return this.obj; }
}
class Geeks {
public static void main(String[] args)
{
// instance of Integer type
Test<Integer> iObj = new Test<Integer>(15);
System.out.println(iObj.getObject());
= new Test<String>("GeeksForGeeks");
System.out.println(sObj.getObject());
iObj = sObj; // This results an error
}
}
Output:
error:
incompatible types:
Test cannot be converted to Test
Explanation: Even though iObj and sObj are of type Test, they are the references to
different types because their type parameters differ. Generics add type safety through
this and prevent errors.
Benefits of Generics
Programs that use Generics has got many benefits over non-generic code.
1. Code Reuse: We can write a method/class/interface once and use it for any type we
want.( Same code can be reused for different types.)
2. Type Safety: Generics make errors to appear compile time than at run time (It's
always better to know problems in your code at compile time rather than making your
code fail at run time).( Catch errors at compile time instead of runtime)
Binding
Binding is a mechanism creating link between method call and method actual
implementation. As per the polymorphism concept in Java , object can have many different
forms. Object forms can be resolved at compile time and run time. If linking between
method call and method implementation is resolved at compile time then we call it static
binding or If it is resolved at run time then it dynamic binding. Dynamic binding uses object
to resolve binding but static binding use type of the class and fields.
Difference between Static binding and dynamic binding in Java
Resolve static binding use type of the class Dynamic binding uses object to
2
mechanism and fields resolve binding
Type of private, final and static methods and Virtual methods use dynamic
4.
Methods variables uses static binding binding
Static Binding
The binding which can be resolved at compile time by the compiler is known as static or
early binding. The binding of all the static, private, and final methods is done at compile-
time.
Example:
// Java Program to Illustrate Static Binding
// Main class
class NewClass {
// print statement
System.out.println("print() in subclass is called");
}
}
Output
print() in superclass is called
print() in superclass is called
Output Explanation: As you can see, in both cases the print method of the superclass is
called. Let us discuss how this happens
We have created one object of subclass and one object of the superclass with the
reference of the superclass.
Since the print method of the superclass is static, the compiler knows that it will not
be overridden in subclasses and hence compiler knows during compile time which
print method to call and hence no ambiguity.
Dynamic Binding
// Main class
public class GFG {
// Print statement
System.out.println(
"print in superclass is called");
}
}
// Print statement
System.out.println(
"print in subclass is called");
}
}
Output
print in superclass is called
print in subclass is called
Output Explanation: Here the output differs. But why? Let's break down the code and
understand it thoroughly.
Methods are not static in this code.
During compilation, the compiler has no idea as to which print has to be called since
the compiler goes only by referencing variable not by the type of object, and therefore
the binding would be delayed to runtime and therefore the corresponding version of
the print will be called based on type on an object.
Typecasting in Java
Typecasting is the process of converting one data type to another data type.
Note: Widening type casting is also sometimes called upcasting for primitives, but it is not
correct to call it casting down.
Syntax:
larger_data_type variable_name = smaller_data_type_variable;
class Geeks {
public static void main(String[] args)
{
int i = 10;
Output
Integer: 10
Long: 10
Double: 10.0
Syntax:
smaller_data_type variable_name = (smaller_data_type) larger_data_type_variable;
class Geeks {
public static void main(String[] args)
{
double i = 100.245;
Output
Original Value before Casting100.245
After Type Casting to short 100
After Type Casting to int 100
1. Explicit Upcasting
Upcasting is the process of casting a subtype to a supertype in the inheritance
tree's upward direction. When a sub-class object is referenced by a superclass
reference variable, an automatic process is triggered without any further effort.
class Animal {
public void makeSound()
{
System.out.println("The animal makes a sound");
}
}
Output
2. Explicit Downcasting
When a subclass type refers to an object of the parent class, the process is referred to as
downcasting. If it is done manually, the compiler issues a runtime ClassCastException
error. It can only be done by using the instanceof operator. Only the downcast of an object
that has already been upcast is possible.
class Geeks {
public static void main(String[] args)
{
Animal animal = new Cat();
animal.eat();
// Explicit downcasting
Cat cat = (Cat)animal;
cat.meow();
}
}
Output
instanceof operator
Syntax:
Example:
class Animal {}
class Dog extends Animal {}
Key Points:
1. Checks inheritance hierarchy:
instanceof returns true if the object is an instance of the specified class or
its subclass, or implements the interface.
In Java, the following some important observations about abstract classes are as follows:
1. An instance of an abstract class can not be created.
2. Constructors are allowed.
3. We can have an abstract class without any abstract method.
4. There can be a final method in abstract class but any abstract method in
class(abstract class) can not be declared as final or in simpler terms final method can
not be abstract itself as it will yield an error: "Illegal combination of modifiers:
abstract and final"
5. We can define static methods in an abstract class
6. We can use the abstract keyword for declaring top-level classes (Outer class) as
well as inner classes as abstract
7. If a class contains at least one abstract method then compulsory should declare a
class as abstract
8. If the Child class is unable to provide implementation to all abstract methods of
the Parent class then we should declare that Child class as abstract so that the next
level Child class should provide implementation to the remaining abstract method
System.out.println(name);
System.out.println(age);
System.out.println(salary);
}
}
// Base class
class Base {
public static void main(String args[])
{
Sunstar s = new Employee();
s.printInfo();
}
}
Output
Anish
12
222.2
Java Interface
In other words, interfaces primarily define methods that other classes must
implement.
An interface in Java defines a set of behaviours that a class can implement, usually
representing a CAN-DO relationship, but not always in every scenario.
Example: This example demonstrates how an interface in Java defines constants and
abstract methods, which are implemented by a class.
import java.io.*;
// Interface Declared
interface testInterface {
class Geeks
{
public static void main(String[] args)
{
TestClass t = new TestClass();
t.display();
System.out.println(t.a);
}
}
Output
Geek
10
Note: In Java, the abstract keyword applies only to classes and methods, indicating that
they cannot be instantiated directly and must be implemented. When we decide on a type of
entity by its behaviour and not via attribute we should define it as an interface.
Syntax:
interface InterfaceName {
// Constant fields (public static final by default)
int CONSTANT = 10;
// Abstract method (public abstract by default)
void methodName();
// Default method (JDK 8+)
default void defaultMethod() {
System.out.println("Default implementation");
}
// Static method (JDK 8+)
static void staticMethod() {
System.out.println("Static method in interface");
}
// Private method (JDK 9+)
private void privateMethod() {
System.out.println("Private helper method");
}
}
Note:
Private methods can only be called inside default or static methods in the interface, not
by implementing classes
Static methods are also accessible via the3interface itself not through objects
To declare an interface, use the interface keyword. It is used to provide total abstraction.
That means all the methods in an interface are declared with an empty body and are
public and all fields are public, static and final by default. A class that implements an
interface must implement all the methods declared in the interface. To implement the
interface, use the implements keyword.
Java Packages
Packages are used in Java in order to prevent naming conflicts, control access, make
searching/locating and usage of classes, interfaces, enumerations, and annotations easier,
etc.
You can define your own packages to bundle groups of classes/interfaces, etc. It is a good
practice to group related classes implemented by you so that a programmer can easily
determine that the classes, interfaces, enumerations, and annotations are related.
Since the package creates a new namespace there won't be any name conflicts with names
in other packages. Using packages, it is easier to provide access control and it is also easier
to locate the related classes.
While creating a package, you should choose a name for the package and include
a package statement along with that name at the top of every source file that contains the
classes, interfaces, enumerations, and annotation types that you want to include in the
package.
The package statement should be the first line in the source file. There can be only one
package statement in each source file, and it applies to all types in the file.
If a package statement is not used then the class, interfaces, enumerations, and annotation
types will be placed in the current default package.
To compile the Java programs with package statements, you have to use -d option as shown
below.
Then a folder with the given package name is created in the specified destination, and the
compiled class files will be placed in that folder.
Let us look at an example that creates a package called animals. It is a good practice to use
names of packages with lower case letters to avoid any conflicts with the names of classes
and interfaces.
Now, let us implement the above interface in the same package animals −
package animals;
/* File name : MammalInt.java */
public class MammalInt implements Animal {
public void eat() {
System.out.println("Mammal eats");
}
public void travel() {
System.out.println("Mammal travels");
}
public int noOfLegs() {
return 0;
}
public static void main(String args[]) {
MammalInt m = new MammalInt();
m.eat();
m.travel();
}
}
interface Animal {
public void eat();
public void travel();
}
Output
$ javac -d . Animal.java
$ javac -d . MammalInt.java
Now a package/folder with the name animals will be created in the current directory and
these class files will be placed in it as shown below.
You can execute the class file within the package and get the result as shown below.
Mammal eats
Mammal travels
Question Bank
2 Marks
5/10 Marks
1. Explain Inheritance.
2. Explain Polymorphism.
3. Explain Super and sub class.
4. Explain Dynamic binding.
5. Explain Generic programming.
6. Explain Abstract class