Skip to content
Java inner classes 6 min read

Nested Interface

A nested interface is an interface declared inside another class or interface. It’s a clean way to tie a contract directly to the type that owns it — think of the Map.Entry interface you use every time you iterate over a HashMap, or a Clickable.OnClickListener callback pattern you’ll recognize from GUI toolkits.

What Is a Nested Interface?

Just like you can nest a class inside another class, you can declare an interface inside a class or inside another interface. The syntax is exactly what you’d expect:

class Outer {
    interface Printer {
        void print(String message);
    }
}

You then implement the nested interface using its fully qualified name: Outer.Printer.

class ConsolePrinter implements Outer.Printer {
    @Override
    public void print(String message) {
        System.out.println(message);
    }
}

public class Main {
    public static void main(String[] args) {
        Outer.Printer p = new ConsolePrinter();
        p.print("Hello from a nested interface!");
    }
}

Output:

Hello from a nested interface!

Note: Regardless of whether you declare a nested interface public, protected, or with package-private access, the interface itself is always implicitly static. You do not need to write the static keyword — but you can. This means a nested interface never holds a reference to an enclosing instance, unlike a non-static member inner class.

Interface Nested Inside a Class

When you put an interface inside a class, you’re signalling: “this contract belongs to this class.” The most famous Java standard-library example is Map.Entry<K,V> from java.util.Map — it defines what a single key-value pair inside a Map looks like.

Here’s a realistic pattern — a Button class that defines its own listener contract:

class Button {
    private String label;

    public Button(String label) {
        this.label = label;
    }

    // The callback contract lives right here, next to Button
    public interface OnClickListener {
        void onClick(Button source);
    }

    private OnClickListener listener;

    public void setOnClickListener(OnClickListener listener) {
        this.listener = listener;
    }

    public void click() {
        System.out.println(label + " was clicked");
        if (listener != null) {
            listener.onClick(this);
        }
    }

    public String getLabel() {
        return label;
    }
}

Now a caller can wire up the callback cleanly:

public class Main {
    public static void main(String[] args) {
        Button btn = new Button("Submit");

        // Implement the nested interface inline (anonymous class)
        btn.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(Button source) {
                System.out.println("Handler: " + source.getLabel() + " clicked!");
            }
        });

        btn.click();
    }
}

Output:

Submit was clicked
Handler: Submit clicked!

Tip: Button.OnClickListener has exactly one abstract method, so it is a functional interface. In Java 8+ you can replace the anonymous class with a lambda: btn.setOnClickListener(source -> System.out.println(source.getLabel() + " clicked!"));

Interface Nested Inside Another Interface

You can also declare an interface inside another interface. This is less common, but it’s how Java models hierarchical contracts — each sub-contract stays grouped with the parent that defines the domain.

interface Vehicle {
    void start();

    // Nested interface — a contract for an engine within any Vehicle
    interface Engine {
        void ignite();
        int getHorsePower();
    }
}

A class can implement either contract independently:

class Car implements Vehicle, Vehicle.Engine {
    @Override
    public void start() {
        System.out.println("Car starting...");
    }

    @Override
    public void ignite() {
        System.out.println("Engine igniting");
    }

    @Override
    public int getHorsePower() {
        return 250;
    }
}

public class Main {
    public static void main(String[] args) {
        Car car = new Car();
        car.start();
        car.ignite();
        System.out.println("HP: " + car.getHorsePower());
    }
}

Output:

Car starting...
Engine igniting
HP: 250

A nested interface inside another interface is implicitly public and static — the same rules as when nested inside a class.

Access Modifiers for Nested Interfaces

When a nested interface lives inside a class, it can carry any access modifier:

ModifierVisibility
publicAccessible from anywhere
protectedAccessible within same package + subclasses
(none)Package-private — same package only
privateOnly the enclosing class can use it

A private nested interface is handy when the contract is purely an internal implementation detail:

class DataProcessor {
    // Hidden from the outside world
    private interface Transformer {
        String transform(String input);
    }

    private static class UpperCaseTransformer implements Transformer {
        @Override
        public String transform(String input) {
            return input.toUpperCase();
        }
    }

    public String process(String data) {
        Transformer t = new UpperCaseTransformer();
        return t.transform(data);
    }
}

public class Main {
    public static void main(String[] args) {
        DataProcessor dp = new DataProcessor();
        System.out.println(dp.process("hello"));
    }
}

Output:

HELLO

When a nested interface lives inside another interface, it is always public — you cannot restrict it further.

Real-World Example: Map.Entry

The best-known nested interface in the Java standard library is Map.Entry<K,V>. It represents a single mapping inside a Map and is declared directly inside the Map interface. You use it whenever you iterate a HashMap:

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> scores = new HashMap<>();
        scores.put("Alice", 95);
        scores.put("Bob", 87);

        // Map.Entry is a nested interface inside Map
        for (Map.Entry<String, Integer> entry : scores.entrySet()) {
            System.out.println(entry.getKey() + " -> " + entry.getValue());
        }
    }
}

Output:

Alice -> 95
Bob -> 87

Because Entry is declared inside Map, the qualified name makes the relationship unmistakable: an entry belongs to a map.

Under the Hood

Always Static at the Bytecode Level

The Java Language Specification mandates that every nested interface is implicitly static. The compiler enforces this: you cannot instantiate a nested interface as if it held an outer reference (the way a non-static inner class does). This means no hidden this$0 synthetic field is generated, so nested interfaces are lightweight at runtime.

You can verify this yourself with the javap tool: compile a class containing a nested interface and run javap -v Outer — the nested interface appears as a member with the ACC_STATIC and ACC_INTERFACE flags.

Separate .class Files

Like all nested types, the compiler produces a separate .class file for each nested interface. A nested interface Printer inside Outer produces:

Outer.class
Outer$Printer.class

The $ separator is a compiler convention. The JVM has no concept of “nesting” — it just loads two independent classes. The InnerClasses attribute in the bytecode records the nesting relationship so tools like IDEs and the reflection API can reconstruct it.

Implementing a Nested Interface Reflectively

Because nested interfaces are just regular interfaces at the JVM level, reflection treats them normally. You can call Button.OnClickListener.class.getName() and get "Button$OnClickListener", and Button.OnClickListener.class.getEnclosingClass() returns Button.class.

When to Use a Nested Interface

Use a nested interface when:

  • The contract is tightly coupled to the enclosing type (callbacks, listeners, entry types).
  • You want the qualified name to make the relationship self-documenting (Map.Entry, Button.OnClickListener).
  • You need to hide an internal contract with private access.

Prefer a top-level interface when:

  • Multiple unrelated classes will implement the contract.
  • The interface is part of a public API that shouldn’t require knowing the enclosing class.
  • The contract evolves independently of any single class.

Warning: Avoid deeply nesting interfaces (interface inside interface inside class) — it obscures your design and makes the qualified names unwieldy to read and type.

Last updated June 13, 2026
Was this helpful?