CodeGym /Courses /JAVA 25 SELF /Default methods in interfaces

Default methods in interfaces

JAVA 25 SELF
Level 21, Lesson 2
Available

1. Introduction

Once upon a time (before Java 8) an interface was very strict: you could declare only abstract methods (no implementation) and constants (public static final). That was convenient until one big problem appeared: library evolution.

Imagine this situation

You’ve developed a popular library that has an interface:

public interface Movable {
    void move(int x, int y);
}

Thousands of developers around the world write their classes implementing this interface. A couple of years later you realise everyone is missing a reset() method that returns the object to its initial position. You add it to the interface:

public interface Movable {
    void move(int x, int y);
    void reset();
}

And then the apocalypse begins: all projects using your interface stop compiling! They are now required to implement the new method, but nobody knew about it. Migration turns into pain.

Default methods — the solution!

Java 8 introduced default methods: now you can add a method with an implementation directly to an interface! All existing classes automatically get a standard implementation and their code doesn’t break. And if you want — you can override the method your own way.

2. Syntax of default methods

A default method is a regular method with an implementation inside an interface, marked with the default keyword.

public interface Movable {
    void move(int x, int y);

    default void reset() {
        // Typical implementation: return to the origin
        move(0, 0);
    }
}

Explanation:

  • All interface methods are by default public and abstract, but default methods are not abstract — they have a body.
  • The default keyword is always written before the method’s return type.

What does it look like in a class?

public class Robot implements Movable {
    private int x, y;

    @Override
    public void move(int x, int y) {
        this.x = x;
        this.y = y;
        System.out.println("Robot moved to (" + x + ", " + y + ")");
    }

    // Implementing reset() is optional — the default version will work!
}

Now, if we call reset() on a Robot object, the implementation from the Movable interface will run:

public class Main {
    public static void main(String[] args) {
        Movable robot = new Robot();
        robot.move(10, 20); // Robot moved to (10, 20)
        robot.reset();      // Robot moved to (0, 0)
    }
}

3. Default methods in the standard library

Default methods were added for a reason: to enable evolving large standard Java interfaces without breaking old code.

Example: the List interface (Java 8+)

In Java 8 the List interface gained methods with implementations, for example, forEach, replaceAll, sort:

default void forEach(Consumer<Entity> action) {
    for (Entity e : this) {
        action.accept(e);
    }
}

If you implement your own list and don’t override forEach, it will still work — thanks to the default method.

Learn more about generic types (Consumer<Entity>) at level 26 :P

4. Why do we need default methods?

  • Evolve APIs without breaking code: you can add new methods to an interface without having to implement them in all existing classes.
  • Reusable behaviour templates: you can declare default behaviour so classes can use it or override it.
  • Reduce duplication: if behaviour is the same for most implementations, you don’t need to copy code into every class.

Analogy

Imagine you have a rental agreement (an interface). It used to say: “The tenant must pay for water.” Later you add: “The tenant must pay for electricity.” Without default methods, you’d have to rewrite all agreements with all tenants! With default methods, you just add the clause, and if someone needs to, they can agree on their own terms.

5. Limitations and characteristics of default methods

Default methods cannot override methods of class Object

You cannot declare in an interface a default method whose signature matches equals, hashCode or toString from Object. This prevents confusion: every object in Java already has these methods.

// Compilation error!
interface Broken {
    default boolean equals(Object obj) { return false; }
}

Conflicts between default methods

What if a class implements two interfaces, each of which has a default method with the same signature? The Java compiler will honestly say: “You decide; I don’t know what to do!”

interface A {
    default void hello() { System.out.println("Hello from A"); }
}

interface B {
    default void hello() { System.out.println("Hello from B"); }
}

class C implements A, B {
    // Must resolve the conflict:
    @Override
    public void hello() {
        // You can choose which method to call, or implement your own
        A.super.hello(); // or B.super.hello();
    }
}

If you don’t implement hello() in class C, you’ll get a compilation error.

Default methods can call other interface methods

A default method can call other interface methods, even abstract ones. The only requirement is that the class provides the implementation.

interface Printer {
    void print(String text);

    default void printTwice(String text) {
        print(text);
        print(text);
    }
}

6. Example: evolve an application with a default method

Let’s look at an example of using default methods in the Movable interface:

public interface Movable {
    void move(int x, int y);

    default void reset() {
        move(0, 0);
    }
}

And there is a class Robot implementing this interface:

public class Robot implements Movable {
    private int x = 5;
    private int y = 7;

    @Override
    public void move(int x, int y) {
        this.x = x;
        this.y = y;
        System.out.println("Robot moved to (" + x + ", " + y + ")");
    }

    // We don’t implement reset() — we use the default method!
}

Now let’s try calling both methods:

public class Main {
    public static void main(String[] args) {
        Movable robot = new Robot();
        robot.move(10, 20); // Robot moved to (10, 20)
        robot.reset();      // Robot moved to (0, 0)
    }
}

If we want Robot to reset in a special way — we simply override reset() in the class:

@Override
public void reset() {
    System.out.println("The robot shuts down and returns to base!");
    move(0, 0);
}

7. Default methods and multiple interface implementation

Default methods are especially useful when a class implements several interfaces. But there’s a nuance: if both interfaces have a default method with the same signature, the compiler will require you to resolve the conflict explicitly.

Conflict example

interface A {
    default void show() { System.out.println("A"); }
}
interface B {
    default void show() { System.out.println("B"); }
}
class C implements A, B {
    @Override
    public void show() {
        // Explicitly choose which default method to use
        A.super.show(); // or B.super.show();
    }
}

8. Diagram: how a default-method call works


+-------------------+
|   Movable         |
|-------------------|
| +move(int, int)   | <- abstract method
| +reset()          | <- default method
+-------------------+
         ^
         |
+-------------------+
|   Robot           |
|-------------------|
| +move(int, int)   | <- implements
|                   | (does not implement reset())
+-------------------+
         |
     Call reset()
         |
   The implementation used
   is from the Movable interface
Default-method call: default implementation from the interface

9. Common mistakes when working with default methods

Error #1: trying to make a default method without an implementation.
A default method must have a body! If you write default void foo();, the compiler will immediately say: “Did you forget the curly braces?”

Error #2: conflict of default methods from different interfaces.
If a class implements two interfaces with the same default method, you must resolve the conflict explicitly — otherwise the compiler won’t let the code compile.

Error #3: trying to declare a default method with the signature of a method from Object.
You can’t make equals, hashCode or toString a default method in an interface — only abstract methods with those names.

Error #4: forgetting that default methods aren’t “magic,” just a convenient tool.
Default methods don’t change the principle that an interface is a contract. If the default behavior doesn’t fit — always override the default method in the class.

1
Task
JAVA 25 SELF, level 21, lesson 2
Locked
Smart Device Remote Control ⚙️
Smart Device Remote Control ⚙️
1
Task
JAVA 25 SELF, level 21, lesson 2
Locked
Behavior of different animals in the zoo 🐕
Behavior of different animals in the zoo 🐕
1
Task
JAVA 25 SELF, level 21, lesson 2
Locked
Double Notification System 🔔🔔
Double Notification System 🔔🔔
1
Task
JAVA 25 SELF, level 21, lesson 2
Locked
Integration of modules from different systems 🧩
Integration of modules from different systems 🧩
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION