Java Interfaces, Inheritance & Polymorphism
Object-oriented programming in Java relies heavily on three fundamental concepts: interfaces, inheritance, and polymorphism. These features enable code reuse, flexible design, and the creation of extensible applications that can evolve over time.
Understanding Inheritance
Inheritance allows a class to inherit properties and methods from another class, promoting code reuse and establishing “is-a” relationships between classes.
Basic Inheritance Example
1// Base class (superclass)
2public abstract class Animal {
3 protected String name;
4 protected int age;
5
6 public Animal(String name, int age) {
7 this.name = name;
8 this.age = age;
9 }
10
11 public void eat() {
12 System.out.println(name + " is eating");
13 }
14
15 public void sleep() {
16 System.out.println(name + " is sleeping");
17 }
18
19 // Abstract method - must be implemented by subclasses
20 public abstract void makeSound();
21
22 // Virtual method - can be overridden
23 public void move() {
24 System.out.println(name + " is moving");
25 }
26
27 // Getters
28 public String getName() { return name; }
29 public int getAge() { return age; }
30}
31
32// Derived class (subclass)
33public class Dog extends Animal {
34 private String breed;
35
36 public Dog(String name, int age, String breed) {
37 super(name, age); // Call parent constructor
38 this.breed = breed;
39 }
40
41 @Override
42 public void makeSound() {
43 System.out.println(name + " barks: Woof! Woof!");
44 }
45
46 @Override
47 public void move() {
48 System.out.println(name + " runs on four legs");
49 }
50
51 // Dog-specific method
52 public void wagTail() {
53 System.out.println(name + " is wagging tail happily");
54 }
55
56 public String getBreed() { return breed; }
57}
58
59public class Cat extends Animal {
60 private boolean isIndoor;
61
62 public Cat(String name, int age, boolean isIndoor) {
63 super(name, age);
64 this.isIndoor = isIndoor;
65 }
66
67 @Override
68 public void makeSound() {
69 System.out.println(name + " meows: Meow! Meow!");
70 }
71
72 @Override
73 public void move() {
74 System.out.println(name + " moves gracefully and silently");
75 }
76
77 // Cat-specific method
78 public void purr() {
79 System.out.println(name + " is purring contentedly");
80 }
81
82 public boolean isIndoor() { return isIndoor; }
83}
84
85// Usage example
86public class InheritanceDemo {
87 public static void main(String[] args) {
88 Dog dog = new Dog("Buddy", 3, "Golden Retriever");
89 Cat cat = new Cat("Whiskers", 2, true);
90
91 // Common behavior from Animal class
92 dog.eat();
93 cat.sleep();
94
95 // Polymorphic behavior - same method, different implementations
96 dog.makeSound(); // Barks
97 cat.makeSound(); // Meows
98
99 dog.move(); // Runs on four legs
100 cat.move(); // Moves gracefully
101
102 // Specific behavior
103 dog.wagTail();
104 cat.purr();
105
106 System.out.println("Dog breed: " + dog.getBreed());
107 System.out.println("Cat is indoor: " + cat.isIndoor());
108 }
109}
What to Notice:
extends
keyword establishes inheritance relationshipsuper()
calls parent constructor@Override
annotation indicates method overridingabstract
methods must be implemented by subclassesprotected
fields are accessible to subclasses
Understanding Interfaces
Interfaces define contracts that classes must fulfill, enabling multiple inheritance of behavior and promoting loose coupling in design.
Interface Examples
1// Basic interface
2public interface Flyable {
3 // Constant (implicitly public, static, final)
4 int MAX_ALTITUDE = 50000;
5
6 // Abstract methods (implicitly public and abstract)
7 void takeOff();
8 void land();
9 void fly(int altitude);
10
11 // Default method (Java 8+)
12 default void performPreFlightCheck() {
13 System.out.println("Performing standard pre-flight check");
14 }
15
16 // Static method (Java 8+)
17 static void displayFlightRegulations() {
18 System.out.println("Flight regulations: Max altitude " + MAX_ALTITUDE + " feet");
19 }
20}
21
22public interface Swimmable {
23 void dive();
24 void swim();
25 void surface();
26
27 default void floatOnWater() {
28 System.out.println("Floating peacefully on water");
29 }
30}
31
32// Interface extending another interface
33public interface AquaticBird extends Flyable, Swimmable {
34 void migrateSeasonally();
35
36 // Can override default methods
37 @Override
38 default void performPreFlightCheck() {
39 System.out.println("Performing aquatic bird pre-flight check");
40 System.out.println("Checking waterproofing and wing condition");
41 }
42}
43
44// Class implementing multiple interfaces
45public class Duck extends Animal implements Flyable, Swimmable {
46 private boolean canFly;
47
48 public Duck(String name, int age, boolean canFly) {
49 super(name, age);
50 this.canFly = canFly;
51 }
52
53 @Override
54 public void makeSound() {
55 System.out.println(name + " quacks: Quack! Quack!");
56 }
57
58 // Implementing Flyable interface
59 @Override
60 public void takeOff() {
61 if (canFly) {
62 System.out.println(name + " spreads wings and takes off");
63 } else {
64 System.out.println(name + " cannot fly");
65 }
66 }
67
68 @Override
69 public void land() {
70 if (canFly) {
71 System.out.println(name + " glides down and lands softly");
72 }
73 }
74
75 @Override
76 public void fly(int altitude) {
77 if (canFly && altitude <= MAX_ALTITUDE) {
78 System.out.println(name + " is flying at " + altitude + " feet");
79 } else {
80 System.out.println(name + " cannot fly at that altitude");
81 }
82 }
83
84 // Implementing Swimmable interface
85 @Override
86 public void dive() {
87 System.out.println(name + " dives under water to find food");
88 }
89
90 @Override
91 public void swim() {
92 System.out.println(name + " swims gracefully on the pond");
93 }
94
95 @Override
96 public void surface() {
97 System.out.println(name + " surfaces with a fish in beak");
98 }
99
100 public boolean canFly() { return canFly; }
101}
102
103// Another implementation
104public class Airplane implements Flyable {
105 private String model;
106 private int currentAltitude;
107 private boolean engineRunning;
108
109 public Airplane(String model) {
110 this.model = model;
111 this.currentAltitude = 0;
112 this.engineRunning = false;
113 }
114
115 @Override
116 public void takeOff() {
117 if (engineRunning) {
118 System.out.println(model + " accelerates down runway and takes off");
119 currentAltitude = 1000;
120 } else {
121 System.out.println("Cannot take off - engine not running");
122 }
123 }
124
125 @Override
126 public void land() {
127 System.out.println(model + " approaches runway and lands safely");
128 currentAltitude = 0;
129 }
130
131 @Override
132 public void fly(int altitude) {
133 if (engineRunning && altitude <= MAX_ALTITUDE) {
134 currentAltitude = altitude;
135 System.out.println(model + " flying at " + altitude + " feet");
136 }
137 }
138
139 @Override
140 public void performPreFlightCheck() {
141 System.out.println("Performing comprehensive aircraft pre-flight check");
142 System.out.println("Checking fuel, engines, controls, and instruments");
143 }
144
145 public void startEngine() {
146 engineRunning = true;
147 System.out.println(model + " engine started");
148 }
149
150 public void stopEngine() {
151 engineRunning = false;
152 System.out.println(model + " engine stopped");
153 }
154}
Polymorphism in Action
Polymorphism allows objects of different types to be treated uniformly through common interfaces or base classes.
Polymorphism Examples
1public class PolymorphismDemo {
2
3 // Method that works with any Animal
4 public static void animalBehavior(Animal animal) {
5 System.out.println("\n=== Animal Behavior Demo ===");
6 System.out.println("Animal: " + animal.getName());
7
8 animal.eat();
9 animal.makeSound(); // Polymorphic call - different implementation for each animal
10 animal.move(); // Polymorphic call
11
12 // Runtime type checking
13 if (animal instanceof Dog) {
14 Dog dog = (Dog) animal; // Downcasting
15 dog.wagTail();
16 } else if (animal instanceof Cat) {
17 Cat cat = (Cat) animal; // Downcasting
18 cat.purr();
19 }
20 }
21
22 // Method that works with any Flyable object
23 public static void demonstrateFlight(Flyable flyable) {
24 System.out.println("\n=== Flight Demonstration ===");
25
26 flyable.performPreFlightCheck(); // May call default or overridden method
27 flyable.takeOff();
28 flyable.fly(15000);
29 flyable.land();
30 }
31
32 // Method demonstrating interface polymorphism
33 public static void aquaticActivities(Swimmable swimmer) {
34 System.out.println("\n=== Aquatic Activities ===");
35
36 swimmer.dive();
37 swimmer.swim();
38 swimmer.floatOnWater(); // Default method
39 swimmer.surface();
40 }
41
42 public static void main(String[] args) {
43 // Create different animal objects
44 Animal[] animals = {
45 new Dog("Rex", 4, "German Shepherd"),
46 new Cat("Luna", 3, false),
47 new Duck("Donald", 2, true)
48 };
49
50 // Polymorphism with inheritance
51 for (Animal animal : animals) {
52 animalBehavior(animal); // Same method, different behavior
53 }
54
55 // Polymorphism with interfaces
56 Flyable[] flyables = {
57 new Duck("Daffy", 3, true),
58 new Airplane("Boeing 747")
59 };
60
61 System.out.println("\n" + "=".repeat(50));
62 Flyable.displayFlightRegulations(); // Static interface method
63
64 for (Flyable flyable : flyables) {
65 demonstrateFlight(flyable);
66 }
67
68 // Interface polymorphism
69 Duck versatileDuck = new Duck("Versatile", 1, true);
70
71 // Same object, different interface views
72 aquaticActivities(versatileDuck); // View as Swimmable
73 demonstrateFlight(versatileDuck); // View as Flyable
74 animalBehavior(versatileDuck); // View as Animal
75 }
76}
Advanced OOP Patterns
Template Method Pattern
1// Abstract class defining algorithm structure
2public abstract class DataProcessor {
3
4 // Template method - defines the algorithm structure
5 public final void processData() {
6 loadData();
7 validateData();
8 transformData();
9 saveData();
10 cleanup();
11 }
12
13 // Concrete methods
14 protected void loadData() {
15 System.out.println("Loading data from source");
16 }
17
18 protected void cleanup() {
19 System.out.println("Cleaning up resources");
20 }
21
22 // Abstract methods - subclasses must implement
23 protected abstract void validateData();
24 protected abstract void transformData();
25 protected abstract void saveData();
26
27 // Hook method - subclasses can override if needed
28 protected boolean shouldTransformData() {
29 return true;
30 }
31}
32
33public class CSVDataProcessor extends DataProcessor {
34 private String fileName;
35
36 public CSVDataProcessor(String fileName) {
37 this.fileName = fileName;
38 }
39
40 @Override
41 protected void validateData() {
42 System.out.println("Validating CSV format and column headers");
43 }
44
45 @Override
46 protected void transformData() {
47 if (shouldTransformData()) {
48 System.out.println("Converting CSV data to objects");
49 }
50 }
51
52 @Override
53 protected void saveData() {
54 System.out.println("Saving processed data to database");
55 }
56
57 @Override
58 protected void loadData() {
59 System.out.println("Loading data from CSV file: " + fileName);
60 }
61}
62
63public class JSONDataProcessor extends DataProcessor {
64 private String apiEndpoint;
65
66 public JSONDataProcessor(String apiEndpoint) {
67 this.apiEndpoint = apiEndpoint;
68 }
69
70 @Override
71 protected void validateData() {
72 System.out.println("Validating JSON structure and required fields");
73 }
74
75 @Override
76 protected void transformData() {
77 System.out.println("Parsing JSON and creating domain objects");
78 }
79
80 @Override
81 protected void saveData() {
82 System.out.println("Caching processed data in memory");
83 }
84
85 @Override
86 protected void loadData() {
87 System.out.println("Fetching data from API: " + apiEndpoint);
88 }
89}
Strategy Pattern with Interfaces
1// Strategy interface
2public interface SortingStrategy {
3 void sort(int[] array);
4 String getAlgorithmName();
5}
6
7// Concrete strategies
8public class BubbleSort implements SortingStrategy {
9 @Override
10 public void sort(int[] array) {
11 int n = array.length;
12 for (int i = 0; i < n - 1; i++) {
13 for (int j = 0; j < n - i - 1; j++) {
14 if (array[j] > array[j + 1]) {
15 // Swap elements
16 int temp = array[j];
17 array[j] = array[j + 1];
18 array[j + 1] = temp;
19 }
20 }
21 }
22 }
23
24 @Override
25 public String getAlgorithmName() {
26 return "Bubble Sort";
27 }
28}
29
30public class QuickSort implements SortingStrategy {
31 @Override
32 public void sort(int[] array) {
33 quickSort(array, 0, array.length - 1);
34 }
35
36 private void quickSort(int[] array, int low, int high) {
37 if (low < high) {
38 int pivotIndex = partition(array, low, high);
39 quickSort(array, low, pivotIndex - 1);
40 quickSort(array, pivotIndex + 1, high);
41 }
42 }
43
44 private int partition(int[] array, int low, int high) {
45 int pivot = array[high];
46 int i = low - 1;
47
48 for (int j = low; j < high; j++) {
49 if (array[j] <= pivot) {
50 i++;
51 int temp = array[i];
52 array[i] = array[j];
53 array[j] = temp;
54 }
55 }
56
57 int temp = array[i + 1];
58 array[i + 1] = array[high];
59 array[high] = temp;
60
61 return i + 1;
62 }
63
64 @Override
65 public String getAlgorithmName() {
66 return "Quick Sort";
67 }
68}
69
70// Context class
71public class SortingContext {
72 private SortingStrategy strategy;
73
74 public SortingContext(SortingStrategy strategy) {
75 this.strategy = strategy;
76 }
77
78 public void setStrategy(SortingStrategy strategy) {
79 this.strategy = strategy;
80 }
81
82 public void performSort(int[] array) {
83 System.out.println("Using " + strategy.getAlgorithmName());
84 long startTime = System.nanoTime();
85
86 strategy.sort(array);
87
88 long endTime = System.nanoTime();
89 double duration = (endTime - startTime) / 1_000_000.0; // Convert to milliseconds
90 System.out.println("Sorting completed in " + duration + " ms");
91 }
92}
93
94// Usage example
95public class StrategyPatternDemo {
96 public static void main(String[] args) {
97 int[] data1 = {64, 34, 25, 12, 22, 11, 90};
98 int[] data2 = data1.clone();
99
100 SortingContext context = new SortingContext(new BubbleSort());
101
102 System.out.println("Original array: " + Arrays.toString(data1));
103
104 // Use bubble sort
105 context.performSort(data1);
106 System.out.println("Bubble sorted: " + Arrays.toString(data1));
107
108 // Switch to quick sort
109 context.setStrategy(new QuickSort());
110 context.performSort(data2);
111 System.out.println("Quick sorted: " + Arrays.toString(data2));
112 }
113}
Key OOP Principles
1. Encapsulation
- Private fields with public getters/setters
- Protected members for inheritance
- Package-private for related classes
2. Inheritance
- Code reuse through
extends
- Method overriding with
@Override
- Constructor chaining with
super()
- Abstract classes for partial implementation
3. Polymorphism
- Runtime method dispatch based on actual object type
- Interface contracts for behavior
- Downcasting with
instanceof
checking
4. Abstraction
- Interfaces define what can be done
- Abstract classes provide partial implementation
- Concrete classes provide full implementation
Best Practices
Design Guidelines:
- Favor composition over inheritance when possible
- Program to interfaces, not implementations
- Use abstract classes for shared code among related classes
- Use interfaces for defining contracts and capabilities
- Keep inheritance hierarchies shallow (prefer 3-4 levels max)
- Override
equals()
,hashCode()
, andtoString()
appropriately
Common Pitfalls:
- Deep inheritance hierarchies become hard to maintain
- Overuse of inheritance when composition would be better
- Not using
@Override
annotation (misses compile-time checks) - Inappropriate downcasting without
instanceof
checks - Breaking Liskov Substitution Principle in subclasses
Summary
Java’s OOP features provide powerful tools for creating maintainable, extensible software:
- Inheritance enables code reuse and “is-a” relationships
- Interfaces define contracts and enable multiple inheritance of behavior
- Polymorphism allows uniform treatment of different object types
- Abstract classes provide partial implementations for related classes
Master these concepts to write cleaner, more flexible Java applications that can evolve with changing requirements.