Interfaces & Abstract Classes

Static & Private Interface Methods

15 min Lesson 5 of 14

Static & Private Interface Methods

Java 8 introduced default methods to interfaces. Two further additions completed the picture: static methods (Java 8) and private methods (Java 9). Together they let interfaces carry real utility logic without polluting the classes that implement them.

Static Methods in Interfaces

A static method in an interface belongs to the interface itself, not to any implementing class. You call it with the interface name, just like a static method on a class — InterfaceName.method().

public interface Validator<T> { boolean isValid(T value); // static factory / helper — called as Validator.requireNonNull(...) static <T> void requireNonNull(T value, String fieldName) { if (value == null) { throw new IllegalArgumentException(fieldName + " must not be null"); } } }

Any class can call Validator.requireNonNull(email, "email") without having to implement Validator. Notice that the method is a helper that the interface ships with itself; implementing classes cannot override it.

Static methods are NOT inherited. If EmailValidator implements Validator<String>, you cannot write EmailValidator.requireNonNull(...). The call must go through Validator.requireNonNull(...). This is intentional — it prevents ambiguity when a class implements multiple interfaces that each declare the same static method name.

Why Put Static Helpers on an Interface?

Before Java 8, the standard pattern was a companion utility class — think Collections alongside Collection, or Objects alongside Object. That works, but it separates related code into two files. Interface static methods let you keep the utility logic right next to the contract it supports:

public interface Discount { double apply(double price); // factory static methods — keep creation logic with the contract static Discount percentage(double pct) { return price -> price * (1 - pct / 100); } static Discount fixed(double amount) { return price -> Math.max(0, price - amount); } static Discount none() { return price -> price; } } // usage Discount tenPercent = Discount.percentage(10); System.out.println(tenPercent.apply(200.0)); // 180.0
Use interface static methods as factory methods. They are a clean alternative to a separate DiscountUtils or Discounts helper class. The result is discoverable — any IDE that types Discount. shows the available factories immediately.

Private Methods in Interfaces (Java 9+)

Once default methods arrived, a problem quickly emerged: two default methods sometimes share duplicated logic, but there was nowhere to put a shared helper inside the interface without exposing it as part of the public contract. Java 9 solved this with private methods.

public interface Logger { void log(String message); default void info(String message) { log(format("INFO", message)); } default void warn(String message) { log(format("WARN", message)); } default void error(String message) { log(format("ERROR", message)); } // private helper — implementation detail, not part of the public API private String format(String level, String message) { return "[" + level + "] " + message; } }

The format method is an implementation detail. Implementing classes never see it — it does not appear in the public API. If you later change the format string, you change one method instead of three.

Private Static Methods in Interfaces

You can also make a private method static. The rule mirrors what you already know from classes: a private instance method can only be called from other instance methods (default methods in this case), while a private static method can be called from both default and static methods.

public interface Codec { String encode(String input); String decode(String input); static Codec base64() { return new Base64Codec(); } static boolean isSupportedCharset(String charset) { return isAsciiSubset(charset); // calls private static helper } // private static — usable from static methods of this interface private static boolean isAsciiSubset(String name) { return name != null && name.matches("[A-Za-z0-9\\-]+"); } }
Private methods cannot be called from implementing classes. They exist solely to reduce duplication inside the interface's own default and static methods. Trying to call a private interface method from an implementing class is a compile-time error.

Putting It All Together

Here is a self-contained example that uses all four method kinds — abstract, default, static, and private — in a single interface:

public interface PriceCalculator { // abstract — must be implemented double basePrice(); // default — shared behaviour, can be overridden default double withTax(double taxRate) { return round(basePrice() * (1 + taxRate / 100)); } default double discounted(double pct) { return round(basePrice() * (1 - pct / 100)); } // static factory static PriceCalculator of(double price) { return () -> price; } // private — eliminates duplication between default methods private double round(double value) { return Math.round(value * 100.0) / 100.0; } } // usage PriceCalculator laptop = PriceCalculator.of(999.99); System.out.println(laptop.withTax(15)); // 1149.99 System.out.println(laptop.discounted(10)); // 899.99

Summary

  • Static methods (Java 8) — belong to the interface, not instances; called via Interface.method(); great for factory methods and pure helpers.
  • Private methods (Java 9) — visible only inside the interface; remove duplication across default methods.
  • Private static methods (Java 9) — same as private methods but callable from static methods too.
  • Neither static nor private methods are part of the contract — implementing classes do not inherit or override them.