! Critical Distinction — Class vs. Interface
Before we write a single line of code, we must establish a fundamental difference in philosophy between a class and an interface. This is the most important idea in this entire module.
class defines what an object is.Animal — it is a living creature
Fruit — it is a type of food
Student — it is a person who studies
Car — it is a vehicle
This is the identity — the is-a relationship.
interface defines what an object can do.Flyable — it can fly
Edible — it can be eaten
Comparable — it can be compared
Printable — it can be printed
This is the behavior — the can-do relationship.
A Bird and a Drone have no common identity — Bird is an Animal, Drone is Equipment.
They are not related by class hierarchy. But they share a behavior: both can fly.
An interface captures this shared behavior across completely unrelated classes —
this is something you cannot achieve with inheritance alone.
1 Why Java Has No Multiple Inheritance — The Diamond Problem
To understand why interfaces were introduced in Java, we must first understand a deliberate design decision: Java does NOT support multiple inheritance of classes. This is not a limitation — it is a careful, intentional choice.
In Java, a class can extend only ONE class.
Writing class D extends B, C
is a compile error in Java.
The reason: a famous problem known as the Diamond Problem.
The Diamond Problem 💎 Understanding the Ambiguity
Imagine we have four classes: A, B, C, and D.
Class A has a method greet(). Both B and C inherit from A and
override greet() with different implementations.
Now, if Java allowed multiple inheritance and D extends both B and C:
B overrides greet() | C overrides greet() | No clear answer!
When class D calls d.greet(), the compiler faces an impossible question:
- Should it call B's version: "Hello from B"?
- Or should it call C's version: "Hello from C"?
- Or the original A's version?
There is no clear, unambiguous answer. This is the Diamond Problem — named because the inheritance diagram forms a diamond shape. Java avoids this entirely by forbidding multiple class inheritance.
Java's Design 🆕 Hierarchy Only — Structured and Predictable
Java's solution: allow only single inheritance in a hierarchy. This makes the language structured, predictable, and free of ambiguity.
class D extends B extends C ← ERROR
class GradStudent extends Student ✓
Java's inheritance is like a family tree: one parent, one grandparent, one chain.
This is clear and unambiguous.
Example: GradStudent extends Student extends Person.
This is clean, readable, and predictable — exactly how Java was designed.
But this raises a question: how do we handle objects that share behavior but have no common ancestor?
That is exactly where interfaces come in.
2 The Problem That Hierarchy Cannot Solve
Now let us work through a real design problem that shows exactly why interfaces were introduced. Follow every step carefully — the solution will surprise you.
Our World 🆕 Four Objects
Step 1 📋 First Design — Using Hierarchy
As a good OOP student, you immediately think in hierarchy:
- Apple and Orange are both Fruit → create a
Fruitparent class - Chicken and Tiger are both Animal → create an
Animalparent class
We can use Animal polymorphically for Tiger and Chicken, and Fruit polymorphically for Apple and Orange. Hierarchy works perfectly here — no code duplication.
Step 2 🤔 A New Requirement Appears
🏫 In the classroom...
Now I want to store Apple, Orange, and Chicken in a single array — but only them, not Tiger. What type should this array be?
Object[] can hold anything.
But there is a problem — it can also hold a Tiger!
We want the compiler to guarantee that only edible objects go into our array.Object[] is too broad. We need a precise type.
How do we design this?
Step 3 🤔 Students Try a Design — What Do You Think?
The professor asks the students to try solving this themselves before giving the answer.
One idea the students propose: "Let us make Edible a class,
and let Apple, Orange, and Chicken inherit from it."
🏫 In the classroom — students evaluate the design...
Edible a class,
and Chicken now extends Edible instead of Animal.
Apple and Orange inherit through Fruit.
Tiger stays in the Animal hierarchy separately.Now we can create an
Edible[] array holding Apple, Orange, and Chicken — and Tiger cannot go in!
Is this a good design?
If Chicken now extends
Edible, it no longer extends Animal.
But Chicken and Tiger are both animals — they share many common features:
breathe(), move(), makeSound(), eat(),
sleep(), and maybe hundreds more.If Chicken does not extend
Animal, we must
write all of those features twice — once in Chicken, once in Tiger.
This is code duplication! This violates the whole reason we use inheritance!
This design forces us to choose: either Chicken inherits its animal behaviors, or it is edible — but not both, because Java allows only one parent class.
No matter how we rearrange the hierarchy, we cannot solve this cleanly. This is exactly why we need something completely different — something that is not a class at all.
If Tiger and Chicken share 100 common features as animals,
and Chicken is forced out of the Animal hierarchy
to become edible — we must duplicate all 100 features in both classes.
The bigger the shared feature set, the worse this design becomes.
Hierarchy cannot solve this problem.
Step 4 💥 Why Every Hierarchy Attempt Fails
What if we place howToEat() in a top-level Edible class,
then make Animal and Fruit both extend it?
The full hierarchy looks like this:
Edible[] can hold anything
that is edible. Let's fill an array with what we want to eat…
❌ Tiger becomes Edible — the entire Animal branch is contaminated.
My students — if you try every possible hierarchy design, you will not find a clean solution.
This problem cannot be solved by inheritance hierarchy alone
in a language that supports only single class inheritance.
We need a new concept: a way for objects to share a behavior
even when they have no common class ancestor.
That concept is the Interface.
3 What is an Interface? — Definition and Rules
An interface is NOT a class.
It is a class-like construct — it has a similar look but a completely different purpose.
An interface does not define objects. It defines a set of behaviors
(method headers) that any willing class can promise to implement.
When a class implements an interface, it makes a
promise to the compiler:
"I guarantee that I have all these behaviors."
Rules 📋 The Five Rules of an Interface
public abstract
Every method inside an interface is automatically
public and abstract
— even if you do not write these keywords explicitly.
There is no method body — only the method signature (header).
public static final (constants)
Any variable declared in an interface is automatically a constant. You cannot have regular instance variables — only final, shared constants.
implements — and must override ALL abstract methods
To adopt an interface, a class uses the keyword implements
(not extends).
It then must provide a body for every abstract method in the interface.
If even one method is missing, it is a compile error.
This is Java's powerful answer to multiple inheritance.
A class can only extend one parent class,
but it can implement as many interfaces as needed.
There is no diamond problem because interfaces have no state and no implementation.
An interface can be used as a polymorphic variable type.
An array of type Edible[] can hold any object whose class implements
Edible — and only those objects.
4 Solving the Edible Problem — Full Implementation
Now let us build the complete solution. Here is the final design that solves the problem hierarchy could not:
- Solid arrow with hollow triangle (▶) =
extends(class inheritance) - Dashed arrow with hollow triangle (➪▶) =
implements(interface) <<interface>>= this is an interface, not a class- Tiger extends Animal but does NOT implement Edible — intentional!
- Chicken both extends Animal AND implements Edible — simultaneously!
Step 1 The Edible Interface
Step 2 The Base Classes
Step 3 The Concrete Classes
Step 4 The Polymorphic Test Program
- Apple, Orange, and Chicken share the
Ediblerelationship ✅ - Tiger is correctly excluded — the compiler enforces this automatically ✅
- We use a single
Edible[]array to treat them polymorphically ✅ - Chicken still inherits from Animal — we lost nothing from the hierarchy ✅
- Apple and Orange still inherit from Fruit — no duplication ✅
5 Interface as a Contract — Parallel Development
The second great purpose of interfaces is to serve as a contract — a formal, compiler-enforced agreement between developers that certain behaviors will exist. This enables something very powerful in software engineering.
The Story 📚 Ali and Ahmed — Working in Parallel
🏫 A real software project scenario...
Ali will build the logic classes:
Addition, Multiplication.Ahmed will build the main quiz program that uses these classes.
Problem: Ali says it will take him one week to finish his work. Should Ahmed wait one full week, doing nothing?
Before Ali starts coding, he and Ahmed sit together and define an interface that specifies exactly what Ali's classes must provide. This interface is their contract — their promise to each other.
Ahmed writes his program using the interface as the data type. Ali builds his classes to fulfill the interface. They work simultaneously — and the moment Ali finishes, Ahmed's program works immediately without any changes!
Addition andMultiplication classes.Must implement every
method in the interface.
CONTRACT
before coding starts
Uses the interface as
the variable type.
Works independently of Ali.
Step 1 The Contract Interface
Step 2 Ali's Implementation
Step 3 Ahmed's Quiz Program
- Ahmed used
ArithmeticOperationas the type — never the concrete class - Ali can refactor or improve his implementation — Ahmed's code still works unchanged
- Adding
SubtractionorDivisionrequires only: create new class, implement interface — Ahmed's loop handles it automatically - The interface guarantees: "anything here will have exactly these four methods"
Suppose you have a powerful algorithm (e.g., a sorting algorithm).
You want others to use it, but you need to guarantee
that the objects they pass are comparable to each other.
You publish an interface: Comparable.
You tell them: "If you want to use my algorithm, implement this interface."
This guarantees 100% behavioral compatibility.
The interface is a handshake between your code and theirs —
no surprises, no missing methods, no runtime errors.
6 Multiple Interfaces — Java's Answer to Flexibility
A class in Java can implement more than one interface at the same time. This is Java's safe, clean answer to the need for multiple behaviors — without any diamond problem.
Discussion 💬 What do Chicken and Orange have in common?
Diabetic interface.But notice this: if something is good for diabetics, it must also be edible, right? You cannot eat something that is not food. So
Diabetic
extends Edible.
extends,
not implements.
Any class that implements Diabetic
automatically satisfies Edible too —
it must provide both methods.
interface Diabetic extends Edible
Any class that implements Diabetic
must also implement howToEat() from Edible.
This creates a hierarchy of interfaces.
Design 🍳 Who implements what?
- Solid arrow =
extends(class or interface inheritance) - Dashed arrow =
implements(class implements interface) Diabetic extends Edible→ any class implementingDiabeticmust also providehowToEat()OrangesatisfiesEdiblethrough two paths: viaFruitand viaDiabetic— Java handles this cleanly
Full Code 📄 Implementation
✅ Interface extends interface — Diabetic extends Edible
creates a behavior hierarchy.
✅ Orange implements two behaviors at once:
Edible (through Fruit) + Diabetic (directly).
✅ Chicken gets Edible for free by implementing Diabetic
(since Diabetic extends Edible).
✅ Tiger is untouched — still just an Animal, never sneaks into food arrays.
✅ The Edible[] array now only accepts true food — the type system protects us.
7 Revisiting the Payroll Problem — The Promise We Made in Module 07
In Module 07 — Polymorphism, we built a Staff Payroll System.
We had a hierarchy: StaffMember → Employee (Executive, Hourly) and Volunteer.
We placed pay() in StaffMember so the polymorphic loop could call it on every element.
But this forced Volunteer to have a pay() method returning 0 — even though a volunteer is not paid at all.
The Doctor said: "This is not good design — but we have no better tool yet. We will fix it in the Interfaces chapter."
That time is now. 🌟
The Problem ⚠︎ Volunteer Forced to Have pay()
pay() in
StaffMember was not good design because
Volunteer had to return 0 even though volunteers never get paid.
Can we now fix that?
pay() from StaffMember,
create a Payable interface, and let only
Employee implement it.
Volunteer will have no
pay() method at all — which is the truth!
↳ Employee { pay() → salary }
↳ Executive { pay() → salary+bonus }
↳ Hourly { pay() → hrs×rate }
↳ Volunteer { pay() → 0 } ← forced!
Volunteer is not payable in real life. This is a design smell.
↳ Employee implements Payable
↳ Executive (inherits Payable)
↳ Hourly (inherits Payable)
↳ Volunteer ← no pay() at all ✓
Only paid staff implement Payable. Volunteer is cleanly excluded.
UML 📌 The Clean Design
Payableis an interface — onlyEmployeeimplements itExecutiveandHourlyinheritPayablefromEmployee— no extra arrow neededVolunteerextendsStaffMemberonly — no connection to Payable whatsoever- The payroll loop uses
Payable[]— Volunteer can never sneak in
Full Code 📄 The Fixed Design
In Module 07 we said: "Volunteer having pay() is not good design — but we have no better tool yet."
Now we do. The Payable interface lets us say exactly which classes
are payable — without touching the rest of the hierarchy.
✅ Volunteer has no pay() method — conceptually correct.
✅ Payable[] array only accepts paid staff — the compiler protects us.
✅ The same pattern solved the Tiger/Edible problem — and now the Volunteer/Payable problem.
This is the power of interfaces.
8 Common Mistakes — Avoid These!
An interface is not a class — you cannot create an object directly from it.
If a class implements an interface, it must override every single abstract method. Missing even one is a compile error.
abstract — then a concrete subclass must complete them.
These two are different tools — do not mix them up:
| Feature | Abstract Class | Interface |
|---|---|---|
| Instance variables | ✅ Yes | ❌ No (only constants) |
| Concrete methods | ✅ Yes | ❌ No (Java 7 and below) |
| Constructor | ✅ Yes | ❌ No |
| Multiple allowed per class | ❌ No — one only | ✅ Yes — many! |
| Represents | Incomplete identity | Shared behavior |
| Keyword used by class | extends |
implements |
| Use when... | Objects share identity and some common code | Unrelated objects share a behavior |
★ Summary — What We Learned Today
A Java interface is a class-like construct that defines a set of abstract behaviors (public abstract methods) and constants (public static final variables). It cannot be instantiated directly. A class that implements an interface must override all its abstract methods. An interface can be used as a polymorphic data type, enabling objects of completely unrelated classes to be treated uniformly based on their shared behavior.