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) {
// 内部统计逻辑
}
}与内部类的对比
很多人会问:既然有了私有方法,还需要内部类吗?
| 场景 | 私有方法 | 内部类 |
|---|---|---|
| 接口内部的方法共享 | ✅ 首选 | ❌ 不适合 |
| 需要维护状态 | ❌ 不行 | ✅ 可以 |
| 需要实现多个方法 | ❌ 不行 | ✅ 可以 |
| 需要继承另一个接口 | ❌ 不行 | ✅ 可以 |
私有方法只是方法,不是独立的执行单元。对于需要维护状态或实现复杂逻辑的场景,内部类仍然是更好的选择。
注意事项
JDK 9+ 限定:私有方法只在 JDK 9 及以上版本可用,如果你的代码需要兼容 JDK 8,不能使用这个特性。
私有方法不能是 protected:接口中私有方法只能是
private,不能是protected。因为接口没有继承层次,protected没有意义。不能为抽象方法:私有方法必须有实现,因为接口中的抽象方法需要实现类去实现,而实现类看不到私有方法。
总结
JDK 7 及以前:接口 = 抽象方法(100% 纯虚)
JDK 8: 接口 = 抽象方法 + 默认方法 + 静态方法
JDK 9+: 接口 = 抽象方法 + 默认方法 + 静态方法 + 私有方法每一步进化都在让接口变得更强大、更实用。私有方法消除了默认方法之间的代码重复,让接口的默认实现可以组织得更加优雅。
