Inheritance gets the glory in most Java textbooks. But interfaces do the real work in production code. Spring, Hibernate, Android, almost every serious Java framework leans on interfaces. If you want to understand how real Java applications are wired together, you need to understand interfaces first.
The good news is the idea is simple. It is just a contract.
The Contract Idea
Imagine you run a delivery service in Dhaka. You do not care how a package reaches the customer. You care that it reaches. Whether a Pathao rider drops it off, a courier van delivers it, or a drone airships it, the outcome is the same. You just need a guarantee: "this thing will deliver."
An interface is that guarantee. It is a contract that says "any class that signs this contract promises to provide these methods." The interface does not care how the methods are implemented. It only cares that they exist.
The official Java documentation puts it plainly: "Interfaces form a contract between a class and the outside world." That contract is enforced at compile time. If your class claims to implement an interface but forgets a method, the compiler will refuse to build your code.
Writing Your First Interface
Let us build on the delivery idea. Here is how you define an interface in Java.
public interface Deliverable {
void deliver(String address);
double getDeliveryCharge();
}
Notice what is missing. There are no curly braces after the method names. No method bodies. No logic. Just the method signature and a semicolon. That is intentional. The interface says what should happen, not how it happens.
Think of it as a job description. It lists the responsibilities. It does not tell the employee step by step how to do the work.
Implementing the Contract
Now let us create a class that signs this contract. We use the implements keyword.
public class PathaoRider implements Deliverable {
private String riderName;
public PathaoRider(String riderName) {
this.riderName = riderName;
}
@Override
public void deliver(String address) {
System.out.println(riderName + " is riding to " + address);
}
@Override
public double getDeliveryCharge() {
return 60.0;
}
}
The @Override annotation is optional but recommended. It tells the compiler "I am intentionally implementing a method from the interface." If you misspell the method name, the compiler catches it immediately.
Now let us add a second class that signs the same contract but behaves differently.
public class CourierVan implements Deliverable {
@Override
public void deliver(String address) {
System.out.println("Van dispatched to " + address);
}
@Override
public double getDeliveryCharge() {
return 200.0;
}
}
Both classes promise to deliver and to report a charge. But they do it completely differently. One sends a rider on a motorbike. The other sends a van. The interface does not care. It only holds them to the contract.
Why This Is Powerful
Here is where things get interesting. You can now write code that works with the contract, not the concrete class.
public class DeliveryService {
public void sendPackage(Deliverable method, String address) {
System.out.println("Delivery charge: " + method.getDeliveryCharge() + " taka");
method.deliver(address);
}
}
The sendPackage method accepts any Deliverable. It does not know whether it is a Pathao rider, a courier van, or something you have not invented yet. Tomorrow you add a DroneDelivery class, and this method keeps working without a single change.
This is the core benefit. You program against the contract, and your code stays flexible as the world changes around it. Baeldung has a solid writeup on this pattern called "Programming to an Interface."
A Class Can Sign Multiple Contracts
A Java class can extend only one parent class. But it can implement as many interfaces as it wants.
public class SmartCourier implements Deliverable, Trackable {
// must implement all methods from both interfaces
}
This matters in real applications. Spring's data access layer uses interfaces like Repository and Transactional. Your service classes often implement several of these. A single bKash transaction service might implement both Payable and Auditable. The combinations keep your code modular without the mess of multiple inheritance.
Default Methods: Interfaces Grew Up
Before Java 8, interfaces could only contain method signatures. No logic at all. Java 8 changed that with default methods. You can now provide a default implementation right inside the interface.
public interface Deliverable {
void deliver(String address);
double getDeliveryCharge();
default void printReceipt() {
System.out.println("Receipt for delivery charge: " + getDeliveryCharge());
}
}
Now every class that implements Deliverable gets printReceipt() for free. No extra code needed. The method can call other interface methods because the contract guarantees they exist.
This feature was added so Java could evolve old interfaces without breaking existing code. The Collection interface in the standard library got dozens of new default methods in Java 8, and code written in 2004 kept compiling. The Java Language Specification covers default methods in full detail if you want to go deeper.
Static Methods in Interfaces
Java 8 also let interfaces hold static methods. These are utility methods that belong to the interface itself, not to implementing classes.
public interface Deliverable {
static Deliverable cheapest() {
return new PathaoRider("default");
}
}
You call it like Deliverable.cheapest() without any object. Handy for factory methods and helper functions that naturally belong with an interface.
Interface vs Abstract Class
Beginners ask this all the time. When do I use an interface, and when do I use an abstract class?
Use an abstract class when classes share state (fields) and partial implementation. Think of it as "these objects are the same kind of thing, with some shared behavior."
Use an interface when you want to describe a capability that unrelated classes can have. A Serializable class, a Comparable class, a Deliverable class. They do not need to be the same kind of thing. They just need to share a contract.
The practical rule from the Java tutorials: "prefer interfaces to abstract classes." Interfaces are more flexible. A class can implement many interfaces but extend only one abstract class.
A Common Beginner Mistake
People sometimes try to instantiate an interface directly.
Deliverable d = new Deliverable(); // will not compile
Java refuses. An interface is a contract, not a concrete thing. You cannot build a house from a contract. You build it from a blueprint, and the blueprint is the class. The interface just says "anything built here must support these operations."
The correct way is always to instantiate a concrete class and assign it to the interface type.
Deliverable d = new PathaoRider("Karim");
d.deliver("Dhanmondi 27");
This is called coding to the interface. The variable type is the interface. The actual object is a concrete implementation. You get flexibility now and can swap implementations later without touching the calling code.
Quick Summary
- An interface is a contract. It lists methods a class must implement.
- Interfaces contain method signatures, not bodies. Default and static methods are the exception.
- A class implements an interface using the
implementskeyword. One class can implement many interfaces. - Default methods (Java 8+) let you add logic inside an interface and evolve it without breaking old code.
- Static methods in interfaces are utility functions tied to the interface itself.
- Prefer interfaces over abstract classes for flexibility. Reserve abstract classes for shared state and partial implementation.
- You cannot instantiate an interface directly. Always create a concrete object and assign it to the interface type.
Based on dev.java/learn: Interfaces
Top comments (0)