Skip to content

Interfaces In Depth

Java interfaces are one of those topics where you might think you already know enough, then realize how much depth is hiding beneath the surface. This article goes through everything you need to deeply understand interfaces: what they are, why they exist, how they behave, and every interview question worth knowing.


What an Interface Actually Is

Think about two systems that need to talk to each other. System 2 does a lot of complex work internally. System 1 wants to use System 2's capabilities but has no interest in how System 2 achieves anything internally. So System 2 publishes a short menu of operations: "here are the things you can ask me to do." System 1 reads that menu, makes requests, and lets System 2 handle everything behind the scenes. That menu is the interface.

The key insight here is that System 1 does not need to know how System 2 does anything. The internal complexity of System 2 is completely hidden. All System 1 ever sees is the list of operations it is allowed to call.

A real world example that makes this concrete: think about driving a car. When you press the brake pedal, you are interacting with an interface. The brake pedal is the operation exposed to you. You press it, and the car slows down. You have zero knowledge of whether the car uses disc brakes, drum brakes, hydraulic pressure, regenerative braking, or some combination of all of those. All of that complexity is hidden from you. The pedal is the contract between you and the car.

In Java terms: the car is a class that implements the interface. The brake pedal is a method signature on that interface. You are the system calling that method. You never need to look inside the class.

One sentence summary of what an interface does: an interface defines what a class must do but not how it does it.


How to Declare an Interface

The syntax for an interface is straightforward:

java
// Basic interface declaration
public interface Bird {
    void fly();   // implicitly: public abstract void fly()
    void eat();   // also implicitly public abstract
}

An interface declaration has four parts: the access modifier, the interface keyword, the interface name, and the body.

For the access modifier, only two options exist at the top level: public and default (no modifier, which means package private). You cannot declare a top level interface as protected or private. This is different from a class modifier inside another class, but for a standalone top level interface, it is public or nothing.

An interface can also extend other interfaces. Notice the word "extend," not "implement." An interface extends other interfaces:

java
// An interface that extends multiple parent interfaces
interface NonFlyingBird extends Bird, LivingThing {
    boolean canRun();
}

One important rule: after the extends keyword in an interface, you can only list other interface names. You cannot extend a class from inside an interface definition. If you try, the compiler rejects it immediately.


Why Interfaces Exist: Three Reasons

There are three core reasons Java has interfaces. Interviewers ask about all three. You should be able to explain all three with examples.

Reason One: Achieving Full Abstraction

An interface gives you a way to expose behavior without exposing implementation. Every method inside an interface is just a signature with no body. The implementing class provides the how. The caller only ever sees the what.

Before Java 8, this was absolute: every single method in an interface was abstract with no implementation whatsoever. Java 8 added default and static methods, and Java 9 added private methods. But the core promise of an interface remains: it tells you what a class will do, not how.

This is why interfaces are said to achieve "100% abstraction" (in the pre Java 8 sense). No concrete implementation leaks through.

Reason Two: Polymorphism Through Interface References

An interface can act as a data type. You can declare a variable whose type is an interface, and that variable can hold any object whose class implements that interface.

java
interface Bird {
    void fly();
}

class Eagle implements Bird {
    @Override
    public void fly() {
        System.out.println("Eagle soars high above the clouds");
    }
}

class Hen implements Bird {
    @Override
    public void fly() {
        System.out.println("Hen flutters a few feet off the ground");
    }
}

// Interface used as a data type
Bird b1 = new Eagle();   // b1 holds an Eagle object
Bird b2 = new Hen();     // b2 holds a Hen object

b1.fly();   // prints: Eagle soars high above the clouds
b2.fly();   // prints: Hen flutters a few feet off the ground

// Bird b = new Bird();   // COMPILE ERROR: you cannot instantiate an interface

Here is what happens at runtime when you call b1.fly(). The JVM looks at what actual object b1 is holding. It finds an Eagle object. It then calls Eagle's version of fly(). When you call b2.fly(), the JVM performs the same lookup, finds a Hen, and calls Hen's version of fly().

This runtime decision about which method to invoke is dynamic polymorphism. The same method call resolves differently depending on what object the interface reference actually contains. The compiler does not decide which implementation runs; the JVM decides at runtime.

One thing to remember permanently: you can never create an object of an interface directly. You cannot write new Bird(). An interface has no implementation, so there is nothing for the JVM to construct. You create objects of concrete classes and hold references to them through an interface variable.

Reason Three: Multiple Inheritance Without the Diamond Problem

Java does not allow a class to extend more than one class. This restriction exists because of the diamond problem. Imagine two parent classes, both with a method called canBreathe(). A child class tries to inherit from both. When you call child.canBreathe(), the compiler has no idea which version to use. There is no safe answer. This ambiguity is the diamond problem.

java
// This does NOT compile in Java
// class Crocodile extends WaterAnimal, LandAnimal { }
// Compiler cannot resolve which canBreathe() to use

Interfaces solve this cleanly. When you implement multiple interfaces that both declare the same method, the class is forced to write its own implementation. One implementation. Zero ambiguity:

java
interface WaterAnimal {
    boolean canBreathe();   // just a signature, no body
}

interface LandAnimal {
    boolean canBreathe();   // just a signature, no body
}

// This works perfectly
class Crocodile implements WaterAnimal, LandAnimal {
    @Override
    public boolean canBreathe() {   // one implementation, no confusion
        return true;
    }
}

Crocodile c = new Crocodile();
c.canBreathe();   // unambiguous: Crocodile's own implementation runs

Because interface methods have no body, there is nothing to conflict. The class provides the one and only implementation. The compiler is satisfied. This is exactly why multiple inheritance is possible through interfaces in Java but not through classes.


Methods in Interfaces: What the Compiler Adds for You

When you write a method inside an interface, you write less than what Java actually sees. The compiler silently adds keywords that you did not type.

java
public interface Bird {
    void fly();          // you write this
    // Java actually sees: public abstract void fly();

    public void eat();   // you write this
    // Java actually sees: public abstract void eat();
    // Both forms are identical; both compile fine
}

Every method in an interface is implicitly public. You can write the word public explicitly if you want, but it makes no difference. Whether you write it or omit it, the method is public.

Every method in an interface is implicitly abstract. Again, you can write the word abstract explicitly, but it changes nothing. The method is always abstract.

There is one access modifier you will never use on an interface method: final. Here is why. final on a method means "this method cannot be overridden in any subclass." But the entire purpose of an interface method is to be overridden (implemented) by the class that uses it. Putting final on an interface method is a direct contradiction of the interface's purpose. The compiler forbids it.

You also cannot use protected or private on interface methods (in standard Java before Java 9). Everything in an interface is public. The interface is designed to be a public contract between systems.


Fields in Interfaces: Why They Are Always Constants

Variables declared in an interface are treated differently from variables in a class. Every field in an interface is implicitly public static final.

java
public interface FlightConstants {
    int MAX_HEIGHT_IN_FEET = 2000;
    // Java actually sees: public static final int MAX_HEIGHT_IN_FEET = 2000;

    public static final int MIN_HEIGHT_IN_FEET = 100;
    // Same as above: both declarations are identical
}

public means you can access it from anywhere. static means it belongs to the interface itself rather than to any instance. final means once you set the value, you cannot change it. Combined, public static final makes every interface field a constant.

This makes sense when you think about what an interface is. An interface represents a contract, not a thing with state. State implies mutable values that differ from object to object. An interface cannot be instantiated, so there are no "instances" to hold varying state. Constants are appropriate: fixed values shared by everyone who uses the interface.

If you try to make an interface field private or protected, the compiler rejects it. Fields in an interface are always public. If you try to reassign a field after it is declared, the compiler rejects that too, because it is final.


Implementing an Interface: The Rules

A Concrete Class Must Implement Everything

When a regular (concrete) class declares implements SomeInterface, it must provide a complete method body for every single method declared in that interface. No exceptions. If even one method is missing a body, the compiler gives an error.

java
interface Bird {
    void fly();
    void eat();
    int numberOfLegs();
}

class Eagle implements Bird {
    @Override
    public void fly() {
        System.out.println("Eagle flies");
    }

    @Override
    public void eat() {
        System.out.println("Eagle eats fish");
    }

    @Override
    public int numberOfLegs() {
        return 2;
    }
    // All three methods implemented: compiles fine
}

You Cannot Reduce Visibility

The interface declares a method as public (even if you did not write the word public, the compiler added it). Your implementing class must honor that. You cannot make the method protected or private in the implementing class. That would be reducing the visibility, which is forbidden.

java
interface Bird {
    void fly();   // implicitly public
}

class Parrot implements Bird {
    // protected void fly() { }   // COMPILE ERROR: more restrictive than public
    public void fly() { }          // correct: same visibility as the interface
}

This rule applies to all inheritance in Java, not just interfaces. A subclass cannot reduce the visibility of an overridden method.

An Abstract Class Can Implement Partially

An abstract class that implements an interface does not have to provide implementations for every method. Because it is abstract, it can leave some methods unimplemented. However, the first concrete class in the inheritance chain must provide implementations for all remaining abstract methods.

java
interface Bird {
    boolean canFly();
    int numberOfLegs();
}

// Abstract class implements the interface but only partially
abstract class Eagle implements Bird {
    @Override
    public boolean canFly() {
        return true;   // implemented
    }
    // numberOfLegs() is NOT implemented here; that is allowed because Eagle is abstract

    // An abstract class can also add its own new abstract methods
    abstract int beakLength();
}

// Concrete class must finish EVERYTHING left abstract
class WhiteEagle extends Eagle {
    @Override
    public int numberOfLegs() {
        return 2;   // finishing what Eagle left abstract from Bird
    }

    @Override
    public int beakLength() {
        return 5;   // finishing Eagle's own abstract method
    }
}

The concrete class WhiteEagle walks up the entire inheritance chain and finds every abstract method that still needs a body. It provides bodies for all of them.

A Class Can Implement Multiple Interfaces

Java allows a single class to implement as many interfaces as you need, separated by commas:

java
interface Swimmer {
    void swim();
}

interface Runner {
    void run();
}

interface Flyer {
    void fly();
}

// One class implements three interfaces
class Duck implements Swimmer, Runner, Flyer {
    @Override
    public void swim() { System.out.println("Duck paddles"); }

    @Override
    public void run() { System.out.println("Duck waddles"); }

    @Override
    public void fly() { System.out.println("Duck flaps wings"); }
}

This is multiple inheritance of behavior through interfaces. It is perfectly legal and the main reason Java chose interfaces as the mechanism for multiple inheritance.


Marker Interfaces

A marker interface is an interface with no methods and no fields at all. Its body is empty:

java
public interface Serializable {
    // no methods, no fields; this is intentional
}

public class Employee implements Serializable {
    private String name;
    private int salary;
    // ...
}

So what is the point? A marker interface tags a class. It is a signal to the JVM or to other libraries that says "this class has some special property." The interface itself does not enforce any contract on behavior. It simply marks the class as belonging to a category.

Java's own standard library uses marker interfaces in several places. java.io.Serializable tells the JVM that objects of this class can be converted to a byte stream and saved or sent over a network. java.lang.Cloneable tells the JVM that calling clone() on this object is permitted. If you try to clone an object whose class does not implement Cloneable, you get an exception at runtime.

The marker interface pattern lets external systems use instanceof to check whether an object has been tagged:

java
if (obj instanceof Serializable) {
    // safe to serialize this object
}

From Java 5 onward, annotations became an alternative to marker interfaces for this kind of tagging. But marker interfaces still exist in Java's standard library and still appear in interview questions.


Nested Interfaces

Java allows you to declare an interface inside another interface, or inside a class. These are called nested interfaces.

Interface Inside an Interface

When you nest an interface inside another interface, the nested interface must be public. This follows the general rule that everything inside an interface is public:

java
public interface Bird {
    boolean canFly();   // outer interface method

    // nested interface: must be public
    public interface NonFlyingBird {
        boolean canRun();   // inner interface method
    }
}

A class can choose to implement just the outer interface, just the inner interface, or both. These are independent choices:

java
// Implementing only the outer interface
class Eagle implements Bird {
    @Override
    public boolean canFly() {
        return true;
    }
    // canRun() not required here; Eagle only implements Bird, not Bird.NonFlyingBird
}

// Implementing only the inner interface
class Ostrich implements Bird.NonFlyingBird {
    @Override
    public boolean canRun() {
        return true;
    }
    // canFly() not required here; Ostrich only implements Bird.NonFlyingBird
}

// Implementing both
class Penguin implements Bird, Bird.NonFlyingBird {
    @Override
    public boolean canFly() {
        return false;
    }

    @Override
    public boolean canRun() {
        return true;
    }
}

You access the inner interface using dot notation: Bird.NonFlyingBird. You can also use the inner interface as a reference type:

java
// Inner interface used as a reference variable type
Bird.NonFlyingBird ref = new Ostrich();
ref.canRun();   // JVM looks up actual object type (Ostrich), calls Ostrich's canRun()

Interface Inside a Class

When you nest an interface inside a class, the access modifier rules relax. A nested interface inside a class can be public, protected, private, or default. It follows the same rules as any other class member:

java
public class Bird {
    // nested interface inside a class: can be protected
    protected interface NonFlyingBird {
        boolean canRun();
    }
}

// Implementing the interface nested inside a class
class Ostrich implements Bird.NonFlyingBird {
    @Override
    public boolean canRun() {
        return true;
    }
}

Nested interfaces tend not to appear in everyday code. In practice, the pattern is rarely needed. However, it appears in interview questions as a way to test whether you know the difference in access modifier rules between "nested interface inside an interface" (must be public) and "nested interface inside a class" (any access modifier).


Functional Interfaces: A Preview

A functional interface is an interface that has exactly one abstract method. Nothing more, nothing less on the abstract side:

java
@FunctionalInterface
interface Flyable {
    void fly();   // exactly one abstract method
}

The @FunctionalInterface annotation is optional but recommended. If you add it and then accidentally add a second abstract method, the compiler gives an error. It acts as a safety net.

Functional interfaces are the foundation of Java 8's lambda expressions. When you write a lambda, Java maps it to the single abstract method of a functional interface. This makes code dramatically more concise for scenarios where you need to pass behavior as a value.

The full depth of functional interfaces, lambdas, and the built in functional interfaces in java.util.function will be covered separately. For now, the important thing to know is: a functional interface is defined entirely by having exactly one abstract method, and that single method is what lambda expressions target.


Interface vs Abstract Class: The Ten Differences

This comparison appears in almost every Java interview. Know all ten rows. Be able to explain the reasoning behind each difference.

1. Keyword Used

An abstract class uses the abstract keyword before class:

java
abstract class Animal { }

An interface uses the interface keyword:

java
interface Flyable { }

2. How a Child Connects

A class that extends an abstract class uses extends:

java
class Dog extends Animal { }

A class that uses an interface uses implements:

java
class Eagle implements Flyable { }

3. Types of Methods Allowed

An abstract class can have both abstract methods (signature only, no body) and concrete methods (full implementation):

java
abstract class Animal {
    abstract void makeSound();       // abstract: no body
    void breathe() { /* body */ }    // concrete: has a body
}

An interface, before Java 8, could only have abstract methods. From Java 8 onward, interfaces can also have default and static methods with bodies. Java 9 added private methods. The Java 8 and 9 additions will be covered in the next article.

4. Inheritance Chain

An abstract class can extend one other class (abstract or concrete) and can also implement multiple interfaces:

java
abstract class Eagle extends Bird implements Flyable, Predator { }

An interface can only extend other interfaces, not classes. And it can extend multiple interfaces:

java
interface NonFlyingBird extends Bird, LivingThing { }

5. Variables and State

An abstract class can have any kind of variable: static, non static, final, non final. This means abstract classes can hold mutable state that changes from object to object:

java
abstract class Animal {
    protected String name;       // not static, not final: instance state
    static int count = 0;        // static: shared across all instances
    final int MAX_LEGS = 4;      // final: constant per instance
}

An interface can only have public static final variables, which are constants. An interface cannot hold any mutable state:

java
interface FlightConstants {
    int MAX_HEIGHT = 2000;   // public static final: a constant
}

6. Access Modifiers for Members

In an abstract class, members (methods and variables) can use any access modifier: private, protected, public, or default.

In an interface, all members are public by default. Before Java 9, you could not use any other modifier. Java 9 introduced private methods in interfaces, which we will cover later.

7. Multiple Inheritance

A class can extend only one other class, whether abstract or concrete. Multiple inheritance through classes is not supported.

An interface supports multiple inheritance. A class can implement any number of interfaces. An interface can extend any number of other interfaces.

8. Relationship Between the Two

An abstract class can implement an interface. It does not have to provide implementations for every method; it can leave some abstract for its subclasses to fill in.

An interface cannot implement or extend an abstract class. Interfaces can only extend other interfaces.

9. Constructor

An abstract class has a constructor. You cannot call it directly (you cannot do new Animal()), but the constructor exists and is called via super() from a subclass constructor.

An interface has no constructor. There is no mechanism to construct an interface. Interfaces cannot be instantiated in any form.

10. How Abstract Methods Are Declared

In an abstract class, an abstract method requires the explicit abstract keyword. And the method can have any access modifier except private (because a private method cannot be overridden, which defeats the purpose):

java
abstract class Animal {
    protected abstract void makeSound();   // 'abstract' keyword required
    public abstract void move();
    // private abstract void breathe();    // forbidden: can't override private
}

In an interface, you do not need the abstract keyword. Any method with just a signature is automatically abstract. And the method is always public:

java
interface Bird {
    void fly();   // automatically public abstract
}

Summary Table

PointAbstract ClassInterface
Keywordabstract classinterface
Child keywordextendsimplements
Method typesAbstract and concreteAbstract only (plus default/static in Java 8, private in Java 9)
Inherits fromOne class, many interfacesOnly interfaces (many)
VariablesAny: static or not, final or notAlways public static final
Member visibilityprivate, protected, public, defaultEverything public (Java 9 adds private)
Multiple inheritanceNoYes
RelationshipCan implement interfaceCannot implement abstract class
ConstructorYesNo
Declaring abstract methodsNeeds abstract keyword; can be protected/public/defaultNo keyword needed; always public

When to Choose Each One

Use an interface when you want to define a pure contract that unrelated classes can fulfill. A Bird and an Airplane have nothing in common structurally, but both can implement a Flyable interface. The contract is about capability, not ancestry.

Use an abstract class when you have related classes that share genuine common implementation. An Animal class might implement breathe() with real logic that all animals share, while leaving makeSound() abstract for each specific animal to implement differently.

The modern guidance (Java 8 onward) leans toward interfaces, because default methods now let interfaces provide shared implementation where needed. Reserve abstract classes for situations where you need genuinely shared mutable state or shared implementation that cannot be expressed any other way and cannot be made public.


Interview Questions to Know Cold

Q: Can an interface be instantiated? No. You cannot write new Bird() if Bird is an interface. You create objects of concrete classes and hold them through interface reference variables.

Q: Can an interface method be declared final? No. final means cannot be overridden. An interface method exists precisely to be overridden (implemented) by classes. The two concepts contradict each other.

Q: What is the default access modifier for interface methods?public. Even if you write nothing, the compiler treats every interface method as public abstract.

Q: What is the default access modifier for interface fields?public static final. Every field in an interface is a constant.

Q: Can an interface have a constructor? No. Interfaces cannot be instantiated, so there is no object to construct, and therefore no constructor.

Q: Why can multiple inheritance be done through interfaces but not through classes? With classes, if two parent classes both have a method with the same signature, the child class inherits two conflicting implementations and the compiler cannot choose. With interfaces, there is no implementation to inherit (only signatures). The implementing class is forced to write its own single implementation, eliminating all ambiguity.

Q: What is a marker interface? Give an example. A marker interface is an interface with no methods or fields. It exists purely to tag a class as having some property. Examples: java.io.Serializable and java.lang.Cloneable.

Q: What are the rules for a nested interface declared inside another interface? It must be public. Everything inside an interface is public, and nested interfaces follow the same rule.

Q: What are the rules for a nested interface declared inside a class? It can have any access modifier: public, protected, private, or default. It follows the same rules as any other member of the class.

Q: Can an abstract class implement an interface without implementing all its methods? Yes. Because the abstract class is abstract, it can leave some methods unimplemented. The first concrete subclass in the chain must implement everything that remains abstract.

Q: If a class implements two interfaces that both declare the same method, what happens? The class must provide exactly one implementation of that method. There is no conflict because neither interface provides a body; they only declare the signature. The class's single implementation satisfies both interfaces.

Q: Can an interface extend a class? No. An interface can only extend other interfaces.

Q: What is a functional interface? An interface with exactly one abstract method. It is the basis for lambda expressions in Java 8. The @FunctionalInterface annotation marks it and triggers a compile error if a second abstract method is accidentally added.


Putting It All Together

Interfaces are the mechanism Java uses to express pure contracts between systems. They enable abstraction by hiding implementation details behind a public list of method signatures. They enable polymorphism by allowing one interface reference to point to any object of a class that implements that interface, with the JVM deciding at runtime which implementation to invoke. And they enable multiple inheritance safely by requiring the class to write its own single implementation whenever a conflict in signatures could arise.

Understanding interfaces deeply means understanding not just the syntax but the reasoning. Why are methods public? Because an interface is a public contract. Why are fields constants? Because an interface represents a contract, not mutable state. Why is final forbidden on interface methods? Because the entire value of an interface method is being overridable. Why can an abstract class leave interface methods unimplemented? Because abstract classes are allowed to defer implementation to their subclasses.

Every rule in interfaces has a reason. Once you see the reasoning, the rules stop feeling arbitrary and start feeling inevitable.