CoreJava Java9

Java 9 – Private Methods in Interfaces: Complete Guide with Examples

Java 9 – Private Methods in Interfaces: Complete Guide with Examples

Java 8 allowed interfaces to have default and static methods with implementations. But once you had multiple default methods, you had no clean way to share helper code between them without exposing that helper as a public part of the interface contract. Java 9 solved this by allowing private methods in interfaces.

This tutorial explains why private interface methods were added, how to use them, and the difference between private and private static interface methods.

The Problem Before Java 9

Imagine an interface with two default methods that share some logic. Before Java 9, you had two ugly options:


// Option 1: Duplicate code in both default methods (bad)
interface Validator {
    default boolean validateAge(int age) {
        if (age < 0 || age > 150) {
            System.out.println("[ERROR] Invalid age: " + age);
            return false;
        }
        return true;
    }

    default boolean validateScore(int score) {
        if (score < 0 || score > 100) {
            System.out.println("[ERROR] Invalid score: " + score);  // DUPLICATED
            return false;
        }
        return true;
    }
}

// Option 2: Expose the helper as a default method (exposes internal detail)
interface Validator {
    // forced to make this "public" even though it's an internal helper
    default void logError(String msg) {
        System.out.println("[ERROR] " + msg);
    }
    // ...
}

The Java 9 Solution: Private Methods


package com.java9r;

/**
 * Demonstrates Java 9 private interface methods.
 * Private methods share code between default methods without
 * exposing internal helpers as part of the public API.
 */
interface Validator {

    // Public API – the methods callers use
    default boolean validateAge(int age) {
        if (age < 0 || age > 150) {
            logError("Invalid age: " + age + ". Must be 0-150.");
            return false;
        }
        return true;
    }

    default boolean validateScore(int score) {
        if (score < 0 || score > 100) {
            logError("Invalid score: " + score + ". Must be 0-100.");
            return false;
        }
        return true;
    }

    default boolean validateName(String name) {
        if (name == null || name.trim().isEmpty()) {
            logError("Name cannot be null or empty.");
            return false;
        }
        if (name.length() < 2 || name.length() > 50) {
            logError("Name length must be 2-50 characters. Got: " + name.length());
            return false;
        }
        return true;
    }

    // Private method – shared by all default methods above
    // NOT part of the public interface contract
    private void logError(String message) {
        System.out.println("[VALIDATION ERROR] " + message);
    }
}

Using the Interface


package com.java9r;

/**
 * Concrete class implementing the Validator interface.
 * Note: logError() is not accessible from StudentValidator.
 */
class StudentValidator implements Validator {
    // No need to implement anything – all default methods are inherited
    // logError() is private – not visible here
}

public class PrivateInterfaceMethodDemo {

    public static void main(String[] args) {

        var validator = new StudentValidator();

        System.out.println("=== Age Validation ===");
        System.out.println(validator.validateAge(25));    // true
        System.out.println(validator.validateAge(-5));    // false
        System.out.println(validator.validateAge(200));   // false

        System.out.println("\n=== Score Validation ===");
        System.out.println(validator.validateScore(85));  // true
        System.out.println(validator.validateScore(110)); // false
        System.out.println(validator.validateScore(-1));  // false

        System.out.println("\n=== Name Validation ===");
        System.out.println(validator.validateName("Ravi"));  // true
        System.out.println(validator.validateName(""));      // false
        System.out.println(validator.validateName(null));    // false
    }
}

Expected Output


=== Age Validation ===
true
[VALIDATION ERROR] Invalid age: -5. Must be 0-150.
false
[VALIDATION ERROR] Invalid age: 200. Must be 0-150.
false

=== Score Validation ===
true
[VALIDATION ERROR] Invalid score: 110. Must be 0-100.
false
[VALIDATION ERROR] Invalid score: -1. Must be 0-100.
false

=== Name Validation ===
true
[VALIDATION ERROR] Name cannot be null or empty.
false
[VALIDATION ERROR] Name cannot be null or empty.
false

Private Static Methods in Interfaces

Java 9 also allows private static methods in interfaces. These are used by static methods in the interface (whereas non-static private methods are used by default instance methods):


interface MathUtils {

    // Public static method
    static double circleArea(double radius) {
        validate(radius, "radius");  // calls private static helper
        return Math.PI * radius * radius;
    }

    static double rectangleArea(double width, double height) {
        validate(width, "width");
        validate(height, "height");
        return width * height;
    }

    // Private static helper – shared by static methods above
    private static void validate(double value, String name) {
        if (value <= 0) {
            throw new IllegalArgumentException(name + " must be positive, got: " + value);
        }
    }
}

// Usage:
System.out.println(MathUtils.circleArea(5));       // 78.53...
System.out.println(MathUtils.rectangleArea(4, 6)); // 24.0
// MathUtils.circleArea(-1);  // throws IllegalArgumentException

Four Types of Interface Methods (Java 9)

Method Type Since Inherited? Has body? Purpose
abstract Java 1 Yes (must implement) No Core contract
default Java 8 Yes (optional override) Yes Optional behavior with default impl
static Java 8 No Yes Utility methods on the interface
private Java 9 No Yes Shared helper for default methods
private static Java 9 No Yes Shared helper for static methods

Rules for Private Interface Methods

  • Must have a body (like default and static methods)
  • Cannot be abstract
  • Cannot be public — they are always private
  • Non-static private methods can only be called from default methods in the same interface
  • Private static methods can be called from both static and default methods in the same interface
  • Not inherited by implementing classes or sub-interfaces

Real-World Use Case: Logging Interface


interface AppLogger {

    default void info(String message) {
        log("INFO", message);
    }

    default void warn(String message) {
        log("WARN", message);
    }

    default void error(String message) {
        log("ERROR", message);
    }

    // Private – formats and prints the log line consistently
    private void log(String level, String message) {
        var timestamp = java.time.LocalDateTime.now()
                .format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        System.out.printf("[%s] [%s] %s%n", timestamp, level, message);
    }
}

Summary

Java 9's private interface methods solve the code duplication problem that existed when multiple default or static methods needed to share helper logic. Private methods are invisible to implementing classes and sub-interfaces — they are purely an implementation detail. Use private methods to extract repeated logic from default methods, and private static methods to extract shared logic from static utility methods in an interface. This makes interfaces cleaner, avoids code duplication, and keeps internal helpers out of the public API.

Topics: CoreJava Java9
← Newer Post Older Post →

Comments

https://www.blogger.com/comment/frame/6690124484600543990?po=4953544501402597580&hl=en&saa=85391&origin=https://www.java9r.com