Default methods and why they exist

Java 8 Course · lesson 5 of 16 · 4 min read

Default methods were not a convenience - they were the only way to add stream() to Collection.

Open this lesson in the learning hub

Key points

  • Before 8, adding a method to a published interface broke every implementation on the planet.
  • That is why Collection.stream() could not exist: it would have broken all third-party collections.
  • A default method ships a body, so existing implementors inherit it and keep compiling.
  • The cost: inherit the same default from two interfaces and you must override to resolve it.
  • Use them to evolve an interface, not as a place to hide real logic - that belongs in a class.

Example

import java.util.*;

public class Main {
    interface Logger {
        void log(String msg);                       // implementors must write this
        default void warn(String msg) {             // added later, breaks nobody
            log("WARN  " + msg);
        }
    }

    static class ConsoleLogger implements Logger {
        @Override public void log(String msg) { System.out.println(msg); }
    }

    static class LoudLogger implements Logger {
        @Override public void log(String msg) { System.out.println(msg); }
        @Override public void warn(String msg) { log("!!!!  " + msg.toUpperCase()); }
    }

    public static void main(String[] args) {
        new ConsoleLogger().warn("inherited the default");
        new LoudLogger().warn("overrode the default");

        // The real motivation: Collection gained stream() without breaking anyone
        System.out.println(List.of(1, 2, 3).stream().mapToInt(Integer::intValue).sum());
    }
}

Default methods exist so a published interface can grow without breaking its implementors.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Java 8 Course course, and every lesson in it is listed on the Java 8 Course contents page.