Interface in Java Explained with Examples

  • Last Updated: February 10, 2026
  • By: javahandson
  • Series
img

Interface in Java Explained with Examples

Interface in Java explained with examples for beginners. Learn what an interface is, how it works, default methods, multiple inheritance, common mistakes, and real-world Java use cases.

1. Introduction: Why Interfaces Exist in Java

When we start learning Java, classes feel enough. We create a class, write a few methods, and call them from another class. At first, everything looks simple. However, as our program grows, problems slowly appear. Our code becomes tightly connected. A small change in one place starts affecting many other parts.

Because of this, we need a better way to design our code. We want to describe what our code should do without forcing how it should do it. At the same time, we want to avoid confusion and unexpected behavior. Interfaces solve this exact problem. They help us keep our Java code clean, flexible, and easier to manage, even when our applications become large.

1.1. Avoiding the multiple inheritance problem

Many object-oriented languages allow a class to inherit from multiple classes. Although this approach looks convenient, it often creates confusion. In particular, method conflicts become hard to resolve.

For example, if two parent classes define the same method, the child class must somehow choose one. Because of this, behavior becomes unpredictable. Debugging also becomes harder as the system grows.

Java intentionally avoided this problem. It did not allow multiple inheritance of classes. However, Java still needed a way to share common behavior. Interfaces filled this gap without introducing ambiguity.

1.2. Interfaces as pure contracts

Instead of inheriting implementation, Java chose to inherit contracts. This decision changed how Java code gets structured.

An interface defines what a class can do, not how it does it. Because of this, the interface stays simple and stable. Meanwhile, each class remains free to implement the behavior in its own way.

As a result, code becomes easier to understand. Responsibilities stay clear. Moreover, designs become more flexible over time.

1.3. Enabling loose coupling in large systems

Large systems rarely stay the same. Requirements change, and implementations evolve. Therefore, tight coupling becomes a serious risk.

Interfaces reduce this risk by promoting loose coupling. A class depends on an interface rather than a concrete class. Consequently, implementations can change without breaking dependent code.

This approach also improves testing and maintenance. Developers can replace implementations, introduce mocks, or add new behavior with minimal impact. Over time, this design choice proves essential for building scalable Java applications.

2. What Is an Interface in Java?

Now that we understand why Java needs interfaces, let us clearly define what an interface actually is. At this stage, we should think less about problems and more about how we describe behavior in Java. This shift helps us use interfaces correctly instead of memorising definitions.

2.1. Describing capabilities, not implementation

An interface allows us to describe what a class is capable of doing. It does not contain the actual logic. Instead, it lists method signatures that a class must provide.

Because of this, an interface stays focused on behavior. For example, when we read an interface, we immediately understand what operations are available. However, we do not see how those operations are performed. That responsibility stays with the implementing class.

As a result, interfaces act like a high-level view of our design. They show intent without exposing details.

2.2. A clear agreement in our code

An interface also creates a clear agreement within our codebase. When a class implements an interface, it agrees to follow that definition exactly.

Java checks this agreement during compilation. Therefore, if we forget to implement a method, the compiler alerts us immediately. Because of this safety, we can trust interface-based code more confidently.

In practice, this agreement allows different classes to work together smoothly. As long as they follow the same interface, our code can treat them uniformly. Consequently, our design stays consistent and predictable.

In simple terms, an interface gives structure to our code. It defines expectations clearly, enforces them strictly, and keeps implementation choices flexible. This balance makes interfaces one of the most important building blocks in Java.

3. How Interfaces Work (Simple Example)

Now that we know what an interface represents, let us see how it works in real code. We will keep the example small and clear. The goal here is understanding, not showing clever tricks.

We will look at three parts: an interface, one class that implements it, and a simple usage example.

3.1. Defining a simple interface

First, we define an interface. This interface describes what an object can do. It does not include any logic.

public interface PaymentService {
    void pay(double amount);
}

Here, we define a single method called pay. We only describe the action. We do not explain how the payment happens. Because of this, the interface stays clean and easy to read.

3.2. Implementing the interface in a class

Next, we create a class that implements this interface. This class provides the actual logic.

public class CardPaymentService implements PaymentService {

    @Override
    public void pay(double amount) {
        System.out.println("Paid " + amount + " using card");
    }
}

By using the implements keyword, we tell Java that this class follows the interface. Therefore, we must implement the pay method. If we miss it, the compiler will stop us. As a result, our code stays safe.

3.3. Using the interface in our code

Finally, we use the interface in a simple main method.

public class PaymentApp {

    public static void main(String[] args) {
        PaymentService service = new CardPaymentService();
        service.pay(500.0);
    }
}

Here, we refer to the object using the interface type, not the class type. Because of this, our code depends on the interface. The actual implementation can change later without affecting this code.

This example shows how interfaces work in practice. We define behavior using an interface. We provide logic in a class. Then, we use the interface to interact with the object. This simple pattern forms the foundation of many real-world Java applications.

4. Key Rules of Interfaces

When we begin working with interfaces, we notice that they behave differently from classes. We do not create objects from them. We do not store changing data inside them. Instead, they focus only on defining behavior. Because of this focus, interfaces follow certain consistent patterns. These patterns may feel unfamiliar at first. However, once we understand the reasons behind them, interfaces become easier to read, write, and use correctly.

4.1. Interface methods are public by default

An interface exists to describe behavior that other classes must follow. Because of this, its methods need to be visible everywhere they are used.

If a method were private or protected, other classes could not rely on it. That would defeat the purpose of the interface. Therefore, Java automatically treats all interface methods as public.

As a result, any class that implements an interface must provide public implementations. This rule keeps the contract clear and accessible. Moreover, it ensures consistent usage across different parts of our code.

4.2. Interface fields are public, static, and final

Interfaces do not represent objects. Instead, they represent shared behavior and common definitions. Because of this, fields inside an interface behave like constants.

First, they are static. This means they belong to the interface itself, not to an object. Next, they are final. Their values cannot change. Finally, they are public, so every implementing class can access them.

Because of this design, interface fields usually store fixed values, such as configuration keys or shared limits. As a result, Java prevents misuse by enforcing immutability and global access.

4.3. Interfaces do not have constructors

A constructor exists to create and initialise an object. However, an interface never creates objects.

Since we cannot instantiate an interface, a constructor would serve no purpose. Therefore, Java does not allow constructors inside interfaces.

This restriction keeps the design clean. Interfaces stay focused on defining behavior, while classes handle object creation and state. Because of this separation, our code remains easier to reason about.

In summary, interface rules exist to protect intent. They keep interfaces simple, consistent, and safe to use. Once we understand the reasons behind these behaviors, interfaces feel natural rather than restrictive.

5. Default and Static Methods in Interfaces (Java 8+)

As we work with interfaces in real projects, we eventually face a practical problem. Interfaces need to grow over time. New features get added, and APIs evolve. However, changing an interface used by many classes can easily break existing code. To solve this safely, Java introduced default and static methods in Java 8. These features allow interfaces to evolve without losing their original purpose.

5.1. Default methods: evolution without breaking existing code

Before Java 8, interfaces could only declare methods. Because of this, adding a new method meant every implementing class had to change. In large systems, this became risky and expensive.

To understand this better, let us look at a simple example.

public interface NotificationService {
    void send(String message);
}

Now imagine this interface is already used by many classes. Later, we decide that every notification should also support logging. Without default methods, adding a new method would break all implementations.

Default methods solve this problem.

public interface NotificationService {
    void send(String message);

    default void log(String message) {
        System.out.println("Log: " + message);
    }
}

Because the log method is the default and has an implementation, existing classes continue to work. As a result, we can add new behavior without forcing changes on old code.

Consider an existing implementation.

public class EmailNotificationService implements NotificationService {

    @Override
    public void send(String message) {
        System.out.println("Sending email: " + message);
    }
}

Even though the interface now has a new method, the above EmailNotificationService class does not need any changes.

public class App {
    public static void main(String[] args) {
        NotificationService service = new EmailNotificationService();
        service.send("Hello JavaHandsOn");
        service.log("Email sent");
    }
}
Output: 
Sending email: Hello JavaHandsOn
Log: Email sent

This is how default methods support backward compatibility. Library authors can enhance interfaces safely, while application code remains stable. However, we should not overuse default methods. If they start carrying large or complex logic, interfaces slowly turn into base classes. Therefore, default methods work best when they stay small and focused.

5.2. Static methods: utility logic close to the interface

Java also allows static methods inside interfaces. These methods belong to the interface itself, not to any implementing class. Because of this, they do not participate in inheritance.

Static methods work well for helper or validation logic that naturally belongs with the interface.

public interface MathOperation {

    int operate(int a, int b);

    static boolean isPositive(int value) {
        return value > 0;
    }
}

We call static methods using the interface name.

public class App {
    public static void main(String[] args) {
        boolean result = MathOperation.isPositive(10);
        System.out.println(result);
    }
}
Output: true

This approach keeps related logic close to the interface. At the same time, it avoids pushing utility code into implementing classes. In real-world APIs, default and static methods help interfaces grow without breaking users, as long as we use them with discipline.

6. Multiple Inheritance Using Interfaces

Java does not allow a class to extend more than one class. However, it does allow a class to implement multiple interfaces. This is not a loophole or an exception. Instead, it is a carefully designed feature. Once we understand how interfaces behave, this decision starts to make sense.

6.1. Why Java allows multiple interfaces

Interfaces do not hold state. They do not have instance variables that change over time. Because of this, implementing multiple interfaces does not create conflicts around data.

Each interface simply adds a new capability to a class.

For example, imagine we want a class that can run in a thread and also be saved to disk.

public class Task implements Runnable, Serializable {

    @Override
    public void run() {
        System.out.println("Task is running");
    }
}

Here, Runnable adds the ability to run code in a thread. Serializable adds the ability to convert the object into a stream. These interfaces do not interfere with each other. As a result, Java safely allows this form of multiple inheritance.

Because interfaces focus only on behavior, Java lets us combine them freely. This approach gives us flexibility without complicating the class hierarchy.

6.2. How Java avoids ambiguity

Even though Java allows multiple interfaces, it still needs to avoid ambiguity. Java handles this by pushing responsibility to the implementing class.

Consider two interfaces that declare the same method.

public interface Printer {
    void print();
}

public interface Scanner {
    void print();
}

Now, a class implements both interfaces.

public class AllInOneMachine implements Printer, Scanner {

    @Override
    public void print() {
        System.out.println("Printing document");
    }
}

Here, there is no ambiguity. Both interfaces expect a print method, and the class provides one clear implementation. Because of this, Java knows exactly what to execute.

However, ambiguity can appear when default methods are involved. Since default methods contain behavior, Java requires explicit resolution.

public interface A {
    default void show() {
        System.out.println("From A");
    }
}
public interface B {
    default void show() {
        System.out.println("From B");
    }
}

If a class implements both interfaces, Java forces us to decide.

public class Demo implements A, B {

    @Override
    public void show() {
        A.super.show(); // or B.super.show()
    }
}
Output: From A

Because of this rule, Java never guesses. It makes us resolve the conflict at compile time. As a result, ambiguity never reaches runtime.

In simple terms, Java allows multiple interfaces because they do not carry state. At the same time, Java avoids ambiguity by enforcing clear rules. This balance gives us power without sacrificing clarity or safety.

7. Interface vs Abstract Class (Short Comparison)

By this point, we understand interfaces well. We may also be familiar with abstract classes, especially if we have already worked with them before. The real question now is not what they are, but when we should choose one over the other. This section focuses only on that decision.

If you want a deeper explanation of abstract classes, we already covered that in detail in a separate article here. Here, we will keep things short and practical.

7.1. When an interface is the better choice

We should choose an interface when we want to define capabilities without forcing a class hierarchy. Interfaces work best when multiple unrelated classes need to follow the same contract.

For example, if different classes need to support the same behavior but already extend different parent classes, an interface fits naturally. Moreover, interfaces work well for public APIs, framework design, and loose coupling. Because of this, they are often the first choice when flexibility matters more than shared code.

In simple terms, if we care more about what a class can do than what it is, an interface is usually the right option.

7.2. When an abstract class makes more sense

An abstract class becomes useful when we want to share common logic or state across related classes. It allows us to provide base behavior while still leaving some methods open for subclasses.

However, a class can extend only one abstract class. Therefore, using it tightly couples the design to a specific inheritance structure. Because of this limitation, abstract classes suit scenarios where classes are closely related and clearly belong to the same hierarchy.

If we need shared fields, protected methods, or constructors, an abstract class is often the better choice.

7.3. A simple way to decide

When we feel unsure, a simple rule helps. If we need flexibility, multiple inheritance, and clean contracts, we should start with an interface. However, if we need shared code and controlled extension, an abstract class fits better.

Both tools exist for a reason. Choosing the right one depends on the design problem we are solving, not on preference or habit.

If you want to explore abstract classes in depth, you can refer to our dedicated article on Abstract Classes in Java, where we cover syntax, use cases, and real-world examples in detail.

8. Real-World Use Cases of Interfaces

Interfaces truly show their value when we look at real-world Java code. Many core Java APIs and popular frameworks rely on interfaces to stay flexible and stable. We may not always notice this, but we benefit from it every day. Let us walk through a few well-known examples and see how interfaces help in practice.

8.1. JDBC: database access without tight coupling

When we work with databases in Java, we rarely write code that depends on a specific database like MySQL or Oracle. Instead, we use JDBC interfaces.

For example, consider this simple JDBC code:

Connection connection = DriverManager.getConnection(url, user, password);
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT name FROM users");

Here, Connection, Statement, and ResultSet are all interfaces. The actual objects come from the database driver at runtime.

Because of this design, our code stays the same even if we switch databases. We will only change the JDBC driver and configuration. As a result, Java applications remain portable and easier to maintain. This is a strong example of how interfaces protect our code from external changes.

8.2. Collections: flexibility through interfaces

The Java Collections framework is another place where interfaces play a central role.

Most of the time, we write code like this:

List<String> names = new ArrayList<>();
names.add("Suraj");
names.add("Shweta");

Here, we declare the variable as a List, which is an interface. The implementation happens to be an ArrayList. Later, if our requirements change, we can easily switch implementations.

List<String> names = new LinkedList<>();

The rest of the code remains unchanged.

Because of this approach, we focus on what we need rather than how it is implemented. For example, we can choose an implementation based on performance or memory needs. Therefore, interfaces help us write code that adapts easily as requirements evolve.

The same idea applies to Map, Set, and other collection interfaces across the Java API.

8.3. Spring: loose coupling in application design

Spring takes the idea of interfaces even further. In a typical Spring application, we depend on interfaces instead of concrete classes.

For example, consider a simple service and repository design.

public interface UserRepository {
    User findById(Long id);
}

Now, we create an implementation.

public class DatabaseUserRepository implements UserRepository {

    @Override
    public User findById(Long id) {
        return new User(id, "Suraj");
    }
}

Our service depends only on the interface.

public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User getUser(Long id) {
        return userRepository.findById(id);
    }
}

Because of this design, Spring can inject any implementation it wants. We can switch to a different repository or use a mock during testing. As a result, our code becomes easier to test, maintain, and extend.

8.4. Why these examples matter

In all these cases, the same idea repeats. We write code against interfaces. We delay implementation choices, and we reduce tight coupling.

As a result, our applications become more flexible and more resilient to change. This is why interfaces are not just a basic Java concept. They are a core design tool used across real-world systems, from low-level database access to large enterprise frameworks.

This practical usage is what makes interfaces such a powerful and essential part of Java.

9. Common Mistakes with Interfaces

Interfaces are simple in concept. However, small design mistakes can reduce their value. Many of these mistakes appear when we treat interfaces like regular classes or try to use them everywhere. Let us look at a few common pitfalls and understand why they cause problems.

9.1. Mistakes

a. Treating an interface like an abstract class

One common mistake is using an interface as if it were an abstract class. This usually happens when we start adding logic-heavy default methods or try to share too much behavior.

An interface should describe capabilities, not manage workflows or internal state. When we push complex logic into default methods, the interface slowly turns into a base class. As a result, the design becomes harder to understand and maintain.

If we truly need shared logic, protected helpers, or state, an abstract class is often the better tool. Therefore, we should keep interfaces focused on defining behavior, not implementing it.

b. Adding too many default methods

Default methods solve a specific problem. They help interfaces evolve without breaking existing code. However, overusing them introduces new risks.

When an interface contains many default methods, it becomes difficult to understand its purpose. Moreover, conflicts can arise when a class implements multiple interfaces with overlapping default methods. Because of this, debugging becomes harder, and designs lose clarity.

Default methods work best when they remain small and intentional. If we find ourselves adding many of them, it usually signals that the design needs rethinking.

c. Using an interface when inheritance is required

Sometimes we choose an interface even when inheritance fits better. This happens when classes are closely related and share common behavior or data.

Interfaces cannot hold instance state. They also cannot provide constructors. Because of this, forcing an inheritance-style design through interfaces leads to duplication and awkward code.

If classes clearly belong to the same hierarchy and need shared logic, an abstract class often makes more sense. Therefore, choosing between an interface and inheritance should depend on the relationship between classes, not on habit.

9.2. Simple way to avoid these mistakes

When we feel unsure, we should pause and ask a simple question. Are we defining what a class can do, or are we trying to share how it works?

If the goal is to define behavior, an interface fits well. However, if the goal is sharing logic or structure, inheritance usually works better. Keeping this distinction clear helps us avoid most interface-related design mistakes.

10. Conclusion

Interfaces play a quiet but powerful role in Java. At first, they may look simple. However, as our applications grow, their real value becomes clear. They help us define behavior, reduce tight coupling, and keep designs flexible.

Throughout this article, we saw how interfaces guide structure without forcing implementation. We also explored how Java safely supports multiple inheritance using interfaces. Moreover, we learned how default and static methods allow interfaces to evolve without breaking existing code. These ideas appear repeatedly in real-world APIs, from JDBC and Collections to Spring-based applications.

At the same time, interfaces require discipline. When we treat them like abstract classes or overload them with logic, designs suffer. Therefore, choosing interfaces thoughtfully matters more than using them everywhere.

In practice, we should see interfaces as design tools, not just language features. When we use them to express intent clearly, our code becomes easier to understand, test, and change. This is why interfaces remain one of the most important foundations of clean and scalable Java applications.

Further Reading

Java Language Specification – Interfaces (JLS 9)
https://docs.oracle.com/javase/specs/jls/se21/html/jls-9.html

Default Methods (Oracle Java Tutorials)
https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html

11. FAQs on Interface in Java

Below are some common questions that beginners and experienced developers often have when working with interfaces in Java.

1. Can we create an object of an interface in Java?

No, we cannot create an object of an interface directly. An interface does not provide complete behavior. However, we can create an object of a class that implements the interface and refer to it using the interface type. Because of this, interfaces help us write flexible and loosely coupled code.

2. Can an interface have method implementations?

Post Java 8, yes, an interface can have method implementations using default and static methods. Default methods allow interfaces to evolve without breaking existing code. Static methods help group related utility logic inside the interface. However, we should keep such methods small and meaningful.

3. Can an interface extend another interface?

Yes, an interface can extend one or more interfaces. Because interfaces do not carry state, Java allows this safely. As a result, we can build larger contracts by combining smaller ones.

4. Can a class implement multiple interfaces?

Yes, a class can implement multiple interfaces. This is how Java supports multiple inheritance in a safe way. The class must implement all abstract methods defined by those interfaces.

5. Why do interface methods not have access modifiers?

Interface methods are public by default. Since an interface defines a contract, its methods must be accessible wherever the interface is used. Therefore, Java enforces public visibility automatically.

6. Can an interface contain variables?

Yes, an interface can contain variables. However, they are always public, static, and final. Because of this, they behave like constants rather than instance fields.

7. When should we use an interface instead of a class?

We should use an interface when we want to define behavior without forcing a class hierarchy. Interfaces work best when flexibility, loose coupling, and multiple inheritance are important.

8. Can interfaces have constructors?

No, interfaces cannot have constructors. An interface cannot be instantiated, so object creation does not apply. Constructors belong only to classes.

9. What happens if two interfaces have the same default method?

In this case, the implementing class must resolve the conflict by overriding the method. Java forces this decision at compile time. As a result, ambiguity never reaches runtime.

10. Are interfaces still relevant in modern Java?

Yes, interfaces remain extremely relevant. Modern Java features such as lambdas, functional interfaces, and framework design rely heavily on interfaces. They continue to be a core part of clean and scalable Java design.

Leave a Comment