00:00

Default Methods in Java – Behaviour in Interfaces

A default method is an interface method with a body, declared with the default keyword. Every class that implements the interface inherits it automatically and may override it. Default methods were added in Java 8 so that interfaces could gain new methods without breaking the classes that already implement them.

This tutorial covers the syntax, the interface evolution problem default methods solve, the rules for resolving conflicts (the "diamond problem") and how default methods compare with abstract classes and static interface methods.

1. Syntax and a First Example

Syntax
interface InterfaceName {
    default ReturnType methodName(Parameters) {
        // body: can call the interface's other methods
    }
}
DefaultMethodBasics.java
public class DefaultMethodBasics {
    interface Vehicle {
        String name();
        int wheels();

        default String describe() {                   // inherited by every implementation
            return name() + " with " + wheels() + " wheels";
        }
        default void honk() { System.out.println(name() + ": Beep!"); }
    }

    static class Car implements Vehicle {
        public String name() { return "Car"; }
        public int wheels()  { return 4; }
        // uses both defaults as they are
    }

    static class Bike implements Vehicle {
        public String name() { return "Bike"; }
        public int wheels()  { return 2; }
        @Override public void honk() { System.out.println(name() + ": Ring ring!"); }  // overrides
    }

    public static void main(String[] args) {
        for (Vehicle v : new Vehicle[] { new Car(), new Bike() }) {
            System.out.println(v.describe());
            v.honk();
        }
    }
}
Output
Car with 4 wheels
Car: Beep!
Bike with 2 wheels
Bike: Ring ring!
  • describe() is written once in the interface and works for every vehicle, because it calls the abstract methods each class implements.
  • Car inherits honk(); Bike overrides it.
  • Default methods are implicitly public. They cannot be final, static or synchronized.

2. Why Default Methods Exist: Interface Evolution

Before Java 8, adding a method to a published interface broke every implementing class, because each one suddenly failed to implement the new abstract method. Java 8 needed to add stream and lambda support to Collection, List, Map and others, which millions of classes implement. Default methods solved this:

InterfaceEvolution.java
import java.util.*;

public class InterfaceEvolution {
    // Version 1 of a library interface had only find(); version 2 added findOrDefault()
    interface Repository<T> {
        Optional<T> find(String id);

        // Added later as a default method: old implementations keep compiling
        default T findOrDefault(String id, T fallback) {
            return find(id).orElse(fallback);
        }
    }

    // An "old" implementation written before findOrDefault existed
    static class UserRepo implements Repository<String> {
        private final Map<String, String> users = Map.of("1", "Asha", "2", "Ravi");
        public Optional<String> find(String id) { return Optional.ofNullable(users.get(id)); }
    }

    public static void main(String[] args) {
        UserRepo repo = new UserRepo();
        System.out.println(repo.findOrDefault("1", "guest"));
        System.out.println(repo.findOrDefault("9", "guest"));

        // The JDK did the same in Java 8: forEach, removeIf, sort and getOrDefault are defaults
        List<Integer> nums = new ArrayList<>(List.of(5, 3, 8, 1, 6));
        nums.removeIf(n -> n < 4);                 // default in Collection
        nums.sort(Comparator.reverseOrder());      // default in List
        nums.forEach(n -> System.out.print(n + " "));
        System.out.println();
    }
}
Output
Asha
guest
8 6 5

JDK examples of default methods include Iterable.forEach, Collection.removeIf and stream, List.sort, Map.getOrDefault, Map.computeIfAbsent and Comparator.reversed.

3. Conflicts and the Diamond Problem

A class can implement several interfaces, so it might inherit two default methods with the same signature. Java resolves this with three rules:

  1. Classes win: a method declared in the class or any superclass takes priority over interface defaults.
  2. More specific interface wins: if B extends A and both define the default, B's version is used.
  3. Otherwise the class must override the method; inside, it can call a particular version with InterfaceName.super.method().
DiamondConflict.java
public class DiamondConflict {
    interface Camera { default String start() { return "Camera ready"; } }
    interface Phone  { default String start() { return "Phone ready"; } }

    // Both interfaces supply start(): the class MUST resolve the conflict
    static class SmartPhone implements Camera, Phone {
        @Override
        public String start() {
            return Phone.super.start() + " + " + Camera.super.start();   // pick or combine
        }
    }

    // Rule 1: a class method always wins over an interface default
    static class Device { public String start() { return "Device ready (class wins)"; } }
    static class Tablet extends Device implements Camera { }

    // Rule 2: the more specific interface wins
    interface ProCamera extends Camera { default String start() { return "ProCamera ready (more specific wins)"; } }
    static class Dslr implements Camera, ProCamera { }

    public static void main(String[] args) {
        System.out.println(new SmartPhone().start());
        System.out.println(new Tablet().start());
        System.out.println(new Dslr().start());
    }
}
Output
Phone ready + Camera ready
Device ready (class wins)
ProCamera ready (more specific wins)

Without the override in SmartPhone, compilation fails with "types Camera and Phone are incompatible; class SmartPhone inherits unrelated defaults for start()".

4. Default Methods vs Static Interface Methods

AspectDefault methodStatic method
Keyworddefaultstatic
Called onAn object: list.sort(...)The interface: Comparator.naturalOrder()
Inherited by implementing classesYesNo
Can be overriddenYesNo
Can use this / abstract methodsYesNo

5. Default Methods vs Abstract Classes

Default methods make interfaces look similar to abstract classes, but important differences remain:

  • Interfaces still have no instance state (fields) and no constructors; default methods can only work through other methods.
  • A class can implement many interfaces but extend only one class.
  • Default methods are always public; abstract classes can have protected and private members with state.

Use default methods for convenience behaviour built on the interface's own methods, and an abstract class when you need shared state or constructors.

6. Best Practices

  • Keep default methods simple and based on the interface's abstract methods.
  • Use them to evolve APIs carefully: a new default can still clash with an existing method of the same name in some implementation.
  • Do not provide defaults for equals, hashCode or toString; this is not allowed, because class methods always win.
  • Use private interface methods (Java 9+) to share code between default methods.

7. Summary

  • Default methods are interface methods with bodies, declared with default (Java 8+).
  • Implementing classes inherit them and may override them.
  • They allow interfaces to evolve without breaking existing implementations.
  • Conflicts: class methods win, then the more specific interface; otherwise override and use X.super.m().
  • Interfaces with defaults still have no state or constructors, unlike abstract classes.

Related Tutorials