Skip to content

JDK 9 接口新特性

JDK 8 给接口带来了默认方法和静态方法,打破了「接口只有抽象方法」的刻板印象。JDK 9 在此基础上更进一步,允许接口定义私有方法。

JDK 8 的遗产

在说 JDK 9 之前,先回顾一下 JDK 8 的两个新能力:

默认方法:带实现的普通方法,实现类不用强制实现。

java
public interface Formula {
    double calculate(int a);

    // 默认方法
    default double sqrt(int a) {
        return Math.sqrt(a);
    }
}

静态方法:属于接口本身的工具方法,不依赖实现类。

java
public interface StringUtils {
    static boolean isEmpty(String s) {
        return s == null || s.isEmpty();
    }
}

// 调用
StringUtils.isEmpty("hello");

详细内容可查看 JDK 8 接口新特性

JDK 9 的核心:私有方法

当接口有多个默认方法时,它们之间可能有共同的逻辑。以往这些共同逻辑无处安放,要么重复代码,要么提取到一个工具类。

JDK 9 的私有方法解决了这个问题:

java
public interface Logger {

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

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

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

    // 私有方法:内部复用,不再重复
    private void log(String level, String message) {
        String timestamp = java.time.LocalDateTime.now().toString();
        System.out.println("[" + timestamp + "] " + level + ": " + message);
    }
}

对比一下没有私有方法时的两种妥协方案:

java
// ❌ 方案一:重复代码
default void logInfo(String message) {
    String timestamp = java.time.LocalDateTime.now().toString();
    System.out.println("[" + timestamp + "] INFO: " + message);
}

default void logError(String message) {
    String timestamp = java.time.LocalDateTime.now().toString();
    System.out.println("[" + timestamp + "] ERROR: " + message);
}

// ❌ 方案二:提取到工具类(破坏封装)
default void logInfo(String message) {
    LoggerHelper.log(this, "INFO", message);
}

私有静态方法

JDK 9 还支持私有静态方法,适用于需要复用的是静态逻辑:

java
public interface Formatter {
    static String format(String template, Object... args) {
        return interpolate(template, args);
    }

    private static String interpolate(String template, Object... args) {
        String result = template;
        for (int i = 0; i < args.length; i++) {
            result = result.replace("{" + i + "}", args[i].toString());
        }
        return result;
    }
}

应用场景:接口组合

私有方法最实用的场景是接口的组合设计。假设我们要做一个缓存接口:

java
public interface Cache {

    // 获取缓存值
    String get(String key);

    // 设置缓存
    void put(String key, String value);

    // 删除缓存
    void remove(String key);

    // === 默认方法:复合操作 ===

    // 获取或计算:如果没有则计算并缓存
    default String getOrCompute(String key, java.util.function.Supplier<String> computer) {
        String value = get(key);
        if (value == null) {
            value = computer.get();
            if (value != null) {
                put(key, value);
            }
        }
        return value;
    }

    // 批量获取
    default java.util.List<String> getAll(java.util.List<String> keys) {
        return keys.stream()
                .map(this::get)
                .toList();
    }

    // === 私有方法:辅助逻辑 ===

    // 验证 key 的格式
    private void validateKey(String key) {
        if (key == null || key.isBlank()) {
            throw new IllegalArgumentException("Key cannot be null or blank");
        }
    }

    // 记录命中/未命中(假设有一个指标服务)
    private void recordHit(String key) {
        // 内部统计逻辑
    }
}

与内部类的对比

很多人会问:既然有了私有方法,还需要内部类吗?

场景私有方法内部类
接口内部的方法共享✅ 首选❌ 不适合
需要维护状态❌ 不行✅ 可以
需要实现多个方法❌ 不行✅ 可以
需要继承另一个接口❌ 不行✅ 可以

私有方法只是方法,不是独立的执行单元。对于需要维护状态或实现复杂逻辑的场景,内部类仍然是更好的选择。

注意事项

  1. JDK 9+ 限定:私有方法只在 JDK 9 及以上版本可用,如果你的代码需要兼容 JDK 8,不能使用这个特性。

  2. 私有方法不能是 protected:接口中私有方法只能是 private,不能是 protected。因为接口没有继承层次,protected 没有意义。

  3. 不能为抽象方法:私有方法必须有实现,因为接口中的抽象方法需要实现类去实现,而实现类看不到私有方法。

总结

JDK 7 及以前:接口 = 抽象方法(100% 纯虚)
JDK 8:      接口 = 抽象方法 + 默认方法 + 静态方法
JDK 9+:     接口 = 抽象方法 + 默认方法 + 静态方法 + 私有方法

每一步进化都在让接口变得更强大、更实用。私有方法消除了默认方法之间的代码重复,让接口的默认实现可以组织得更加优雅。

基于 VitePress 构建