Introduction
An interface in Java is a reference type that defines a contract for what a class can do, without specifying how it does it. It contains abstract methods (by default), constants, and, since Java 8, default and static methods. Interfaces enable full abstraction and support multiple inheritance of type, allowing a class to implement multiple interfaces. They are fundamental to achieving loose coupling, polymorphism, and designing flexible, maintainable systems in Java. Understanding interfaces is essential for effective API design, plugin architectures, and modern Java development.
1. What Is an Interface?
- Declared with the
interfacekeyword. - Cannot be instantiated.
- All methods are public and abstract by default (prior to Java 8).
- All fields are public, static, and final (i.e., constants).
- A class implements an interface using the
implementskeyword. - A class can implement multiple interfaces.
Purpose: Define a set of capabilities that implementing classes must provide.
2. Basic Syntax and Declaration
interface InterfaceName {
// Constants (public static final by default)
int MAX_SIZE = 100;
// Abstract method (public abstract by default)
void doSomething();
// Default method (Java 8+)
default void log() {
System.out.println("Logging action");
}
// Static method (Java 8+)
static void info() {
System.out.println("Interface info");
}
}
Example: Drawable Interface
interface Drawable {
void draw(); // abstract method
default void resize() {
System.out.println("Resizing drawable object");
}
static void description() {
System.out.println("This is a drawable shape.");
}
}
3. Implementing an Interface
A class uses the implements keyword to fulfill the contract of an interface.
Rules
- Must implement all abstract methods (unless the class is
abstract). - Can override
defaultmethods. - Cannot override
staticmethods (they belong to the interface itself).
Example
class Circle implements Drawable {
@Override
public void draw() {
System.out.println("Drawing a circle");
}
// Optional: override default method
@Override
public void resize() {
System.out.println("Circle resized");
}
}
class Rectangle implements Drawable {
@Override
public void draw() {
System.out.println("Drawing a rectangle");
}
}
Usage
public class Main {
public static void main(String[] args) {
Drawable circle = new Circle();
Drawable rect = new Rectangle();
circle.draw(); // Drawing a circle
rect.draw(); // Drawing a rectangle
circle.resize(); // Circle resized
Drawable.description(); // This is a drawable shape.
}
}
4. Key Features of Interfaces (Java 8+)
A. Default Methods (default)
- Provide a default implementation for methods.
- Allow adding new methods to interfaces without breaking existing implementations.
- Can be overridden by implementing classes.
Use Case: Evolving APIs (e.g., Java Collections Framework added
forEachvia default method).
B. Static Methods (static)
- Belong to the interface itself (not instances).
- Called using
InterfaceName.method().
C. Private Methods (Java 9+)
- Support helper methods inside interfaces.
- Can be
privateorprivate static.
interface MathOps {
default int add(int a, int b) {
return validate(a) + validate(b);
}
private int validate(int x) {
return x < 0 ? 0 : x; // Helper method
}
}
5. Multiple Inheritance with Interfaces
Java does not support multiple inheritance of state (classes), but it does support multiple inheritance of type via interfaces.
interface Flyable {
void fly();
}
interface Swimmable {
void swim();
}
class Duck implements Flyable, Swimmable {
@Override
public void fly() {
System.out.println("Duck is flying");
}
@Override
public void swim() {
System.out.println("Duck is swimming");
}
}
No diamond problem: If two interfaces have the same default method, the implementing class must override it to resolve ambiguity.
interface A {
default void greet() { System.out.println("A"); }
}
interface B {
default void greet() { System.out.println("B"); }
}
class C implements A, B {
@Override
public void greet() {
A.super.greet(); // Call A's version
// or B.super.greet();
}
}
6. Interfaces vs. Abstract Classes
| Feature | Interface | Abstract Class |
|---|---|---|
| Instantiation | ❌ | ❌ |
| Multiple Inheritance | ✅ (implement many) | ❌ (extend one) |
| State (Instance Fields) | ❌ (only constants) | ✅ |
| Constructors | ❌ | ✅ |
| Method Implementation | Default/static/private (Java 8+) | Full concrete methods |
| Access Modifiers | All methods public | Methods can be private, protected, etc. |
| Use Case | "Can-do" capability, API contracts | "Is-a" relationship with shared code |
Rule of Thumb:
- Use interfaces to define what an object can do.
- Use abstract classes to define what an object is, with shared behavior.
7. Common Use Cases
A. Defining APIs and Contracts
interface PaymentProcessor {
boolean processPayment(double amount);
}
B. Callbacks and Event Handling
interface OnClickListener {
void onClick();
}
C. Marker Interfaces (No Methods)
Indicate special behavior (e.g., Serializable, Cloneable).
public class User implements Serializable {
// Now objects can be serialized
}
D. Functional Interfaces (Java 8+)
Interfaces with exactly one abstract method—used with lambda expressions.
@FunctionalInterface
interface Calculator {
int compute(int a, int b);
}
// Usage with lambda
Calculator add = (a, b) -> a + b;
8. Best Practices
- Prefer interfaces over abstract classes when you only need to specify behavior.
- Use default methods sparingly—only for backward-compatible enhancements.
- Keep interfaces focused and cohesive (Single Responsibility Principle).
- Use
@FunctionalInterfaceto enforce single abstract method (SAM) rule. - Avoid adding too many methods—favor composition or multiple fine-grained interfaces.
9. Common Mistakes
- Trying to instantiate an interface:
Drawable d = new Drawable(); // ❌ Compilation error
- Forgetting to implement abstract methods:
class Square implements Drawable { } // ❌ Must implement draw()
- Assuming interfaces can have instance fields:
interface Bad {
int count = 0; // OK (constant)
// int value; // ❌ Not allowed
}
- Ignoring ambiguity in multiple default methods → must override explicitly.
Conclusion
Interfaces are a cornerstone of flexible and scalable Java design. They enable polymorphism, support multiple inheritance of type, and promote loose coupling between components. With the addition of default, static, and private methods in modern Java, interfaces have become even more powerful—allowing API evolution without breaking existing code. Whether defining service contracts, enabling plugin architectures, or leveraging lambda expressions through functional interfaces, mastering interfaces is essential for writing clean, modular, and future-proof Java applications. Always design interfaces to be minimal, clear, and stable—they are the foundation of your system’s extensibility.