Java OOP Inheritance & Polymorphism
One Family, Many Behaviors
Story: Our application now has Employees. But not every employee behaves the same way. A Developer codes, a Manager manages, and a Designer designs. We need to reuse common behavior while allowing specialized behavior.
1. The Problem
Suppose we have:
Employee
├── Developer
├── Manager
└── DesignerEvery employee has:
name
salary
employeeId
work()But each employee also has specialized behavior.
We don’t want to duplicate common code.
We want to express:
Developer IS-A Employee
Manager IS-A Employee
Designer IS-A EmployeeThis leads to inheritance.
2. Inheritance
Java uses:
extendsExample:
class Employee {
String name;
double salary;
void work() {
System.out.println("Employee working");
}
}class Developer extends Employee {
void writeCode() {
System.out.println("Writing code");
}
}Now:
Developer developer = new Developer();
developer.name = "Amit";
developer.salary = 2400000;
developer.work();
developer.writeCode();3. Parent and Child
Terminology:
Employee
├── Parent
├── Superclass
└── Base class
Developer
├── Child
├── Subclass
└── Derived classAll are commonly used terms.
4. The IS-A Rule
Inheritance should normally represent an IS-A relationship.
Developer IS-A Employee
Manager IS-A Employee
Dog IS-A AnimalBut:
Car IS-A Enginedoesn’t make sense.
A car:
HAS-A EngineThis is composition.
Remember
IS-A
↓
InheritanceHAS-A
↓
Composition5. Parent Constructor Runs First
Consider:
class Employee {
Employee() {
System.out.println("Employee constructor");
}
}class Developer extends Employee {
Developer() {
System.out.println("Developer constructor");
}
}Now:
Developer d = new Developer();Output:
Employee constructor
Developer constructorThe parent part is initialized before the child constructor completes.
6. super()
Java conceptually inserts:
Developer() {
super();
System.out.println("Developer constructor");
}super() calls the parent's no-argument constructor.
7. Parameterized Parent Constructor
If the parent only has:
class Employee {
Employee(String name) {
System.out.println(name);
}
}then the child must explicitly call it:
class Developer extends Employee {
Developer(String name) {
super(name);
}
}Remember:
If a parent doesn’t have an accessible no-argument constructor, the child must explicitly invoke an appropriate parent constructor.
8. super(...) Rule
Just like this(...), super(...) must be the first statement in a constructor.
Correct:
Developer(String name) {
super(name);
}Incorrect:
Developer(String name) {
System.out.println("Starting");
super(name);
}9. super Can Access Parent Behavior
Suppose:
class Employee {
void work() {
System.out.println("Employee working");
}
}Child:
class Developer extends Employee {
@Override
void work() {
super.work();
System.out.println("Developer coding");
}
}super.work() means:
Call the parent implementation of
work().
10. this vs super
this
↓
Current object/current class contextsuper
↓
Parent-class contextConstructor:
this(...)
↓
another constructor in same classsuper(...)
↓
constructor in parent class11. Access and Inheritance
Suppose:
class Employee {
public String name;
private double salary;
protected int employeeId;
}A subclass can directly access:
name
employeeIdbut cannot directly access:
salarybecause salary is private.
Important
Inheritance does not override access control.
private still means private to the declaring class.
12. Protected
protected provides access:
- Within the same package
- To subclasses, subject to Java’s protected-access rules
For everyday understanding:
private
→ declaring class
protected
→ same package + subclass accessWe can revisit exact edge cases later.
13. Method Overriding
Parent:
class Employee {
void work() {
System.out.println("Employee working");
}
}Child:
class Developer extends Employee {
@Override
void work() {
System.out.println("Developer coding");
}
}The child has overridden the parent’s method.
14. @Override
Use:
@OverrideWhenever you intend to override a parent method.
It tells the compiler:
“I expect this method to override something from the parent.”
If the method signature is wrong, the compiler can catch the mistake.
15. Overloading vs Overriding
Overloading
void work()
void work(int hours)Same method name, different parameters.
Usually resolved at compile time.
Overriding
class Employee {
void work() {}
}
class Developer extends Employee {
@Override
void work() {}
}Child provides a new implementation of inherited behavior.
This participates in runtime polymorphism.
16. The Most Important Line
Now we write:
Employee employee = new Developer();This contains two different types.
Employee
↓
Reference / compile-time type
Developer
↓
Actual object / runtime typeThis distinction is the foundation of polymorphism.
17. What Does the Reference Type Control?
Suppose:
class Employee {
void work() {
}
}class Developer extends Employee {
void writeCode() {
}
}Now:
Employee employee = new Developer();This works:
employee.work();because Employee has work().
But:
employee.writeCode();does not compile.
Why?
Because the reference type is:
Employeeand Employee does not declare writeCode().
18. The Golden Rule of Polymorphism
Reference type determines what members you can access.
Actual object type determines which overridden instance method runs.
Example:
Employee employee = new Developer();
employee.work();Compile time:
Does Employee have work()?
YESRuntime:
What is the actual object?
Developer
Does Developer override work()?
YES
Developer.work() runs19. Runtime Polymorphism
This is called runtime polymorphism.
Get Amit Kumar’s stories in your inbox
Join Medium for free to get updates from this writer.
It is based on:
Inheritance
+
Method overriding
+
Parent reference
+
Child objectExample:
Employee e1 = new Developer();
Employee e2 = new Manager();
e1.work();
e2.work();Possible output:
Developer coding
Manager managingThe same reference type can represent different actual objects.
20. Dynamic Method Dispatch
The process can be visualized as:
employee.work()
↓
Compile time
↓
Does Employee have work()?
↓
YES
↓
Runtime
↓
What object is employee pointing to?
↓
Developer
↓
Does Developer override work()?
↓
YES
↓
Developer.work()This is dynamic method dispatch.
21. Why Polymorphism Is Useful
Suppose:
void makeEmployeeWork(Employee employee) {
employee.work();
}We can call:
makeEmployeeWork(new Developer());
makeEmployeeWork(new Manager());
makeEmployeeWork(new Designer());We don’t need separate methods for every employee type.
One abstraction handles many implementations.
22. Upcasting
This is valid:
Developer developer = new Developer();
Employee employee = developer;This is called upcasting.
Developer
↓
EmployeeChild → Parent.
No explicit cast is normally required.
Why is it safe?
Because:
Developer IS-A Employee23. Downcasting
The reverse:
Employee employee = new Developer();
Developer developer =
(Developer) employee;is called downcasting.
Parent reference → Child type.
An explicit cast is required.
24. Downcasting Can Fail
This compiles:
Employee employee = new Manager();
Developer developer =
(Developer) employee;But at runtime:
ClassCastExceptionWhy?
Because the actual object is:
Managernot:
Developer25. instanceof
You can check before downcasting:
if (employee instanceof Developer) {
Developer developer =
(Developer) employee;
}Modern Java also supports:
if (employee instanceof Developer developer) {
developer.writeCode();
}26. Polymorphism with Arrays
This is a beautiful example.
Employee[] employees = {
new Developer(),
new Manager(),
new Designer()
};Then:
for (Employee employee : employees) {
employee.work();
}Each object executes its own implementation.
Developer → Developer.work()
Manager → Manager.work()
Designer → Designer.work()One array.
One reference type.
Multiple behaviors.
27. Real-World Example: Payments
Imagine:
Payment
├── UPI
├── CreditCard
└── WalletEach has:
pay()Then:
Payment payment = new UPIPayment();
payment.pay();The caller doesn’t need to know how UPI works.
It only knows:
Payment
↓
pay()This is polymorphism combined with abstraction.
28. Important Exceptions
Not everything behaves like overridden instance methods.
private
Private methods do not participate in overriding.
final
A final method cannot be overridden.
static
Static methods are hidden, not dynamically overridden.
Fields
Fields are not dynamically dispatched like instance methods.
Example:
class Parent {
String name = "Parent";
}
class Child extends Parent {
String name = "Child";
}
Parent p = new Child();
System.out.println(p.name);This prints:
ParentFields don’t use runtime method dispatch.
29. Single Class Inheritance
Java does not allow:
class C extends A, B {
}A class can directly extend only one class.
But it can implement multiple interfaces:
class C implements A, B {
}This becomes important when we study interfaces.
30. Composition vs Inheritance
Inheritance:
Developer IS-A EmployeeComposition:
Car HAS-A EngineExample:
class Engine {
void start() {
}
}
class Car {
private Engine engine;
void startCar() {
engine.start();
}
}Composition often provides more flexibility and lower coupling.
This leads to the principle:
Favor composition over inheritance when the relationship is not a genuine, stable
IS-Arelationship.
31. The Big Picture
Employee
│
├── Developer
├── Manager
└── DesignerCommon behavior:
name
salary
work()Specialized behavior:
Developer → writeCode()
Manager → manageTeam()
Designer → design()Polymorphism lets us write:
Employee employee = new Developer();and use:
employee.work();while Java executes:
Developer.work()Quick Revision
Inheritance
A mechanism for creating specialized types from a parent type.
extends
Used for class inheritance.
super()
Calls a parent constructor.
super.method()
Calls the parent implementation.
Overriding
Child provides a new implementation of inherited behavior.
Overloading
Same method name with different parameter lists.
Upcasting
Child → Parent.
Downcasting
Parent reference → Child type.
Polymorphism
One abstraction/reference can represent multiple concrete implementations.
Golden Rule
Reference type controls what you can access. Actual object type controls which overridden instance method runs.
Composition
HAS-A relationship.
Inheritance
IS-A relationship.









