java-接口适配器模式 jsk8 接口默认实现
一、接口适配器模式
1-1. 问题背景
有时候,一个接口里可能有很多方法,比如:
public interface Listener {void onStart();void onRun();void onStop();void onError();void onTimeout();// ... 假设有100个方法
}
但是现在类 A 只想用其中 1~2 个方法,如果直接实现接口:
class A implements Listener {@Overridepublic void onStart() {}@Overridepublic void onRun() {}@Overridepublic void onStop() {}@Overridepublic void onError() {}@Overridepublic void onTimeout() {}
}
你会发现:
你必须把所有方法都实现一遍,即使大部分不需要。
非常麻烦。
1-2. 解决办法:接口适配器(Adapter Class)
让 一个类 来实现接口,并把所有方法写成空实现:——适配器类
public abstract class ListenerAdapter implements Listener {public void onStart() {}public void onRun() {}public void onStop() {}public void onError() {}public void onTimeout() {}
}
然后 A 不再直接实现接口,而是 继承这个适配器类:
class A extends ListenerAdapter {@Overridepublic void onRun() {System.out.println("A 自己只关心 onRun");}
}
这样:
A 只需要重写自己关心的方法
不用理会其他 99 个方法
简化实现
1-3. 这种模式叫什么?
接口适配器模式(Interface Adapter Pattern)
又叫:
缺省适配器
默认适配器
抽象适配器模式
本质:用一个抽象类“兜底”,替你把多余的方法空实现掉。
1-4. Spring 里也大量使用这种适配器!
比如你写过:Servlet API:
public class MyServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) {...}
}
为什么你只写 doGet() 就够了?
因为 HttpServlet 已经帮你把其他方法空实现了 —— 完全就是接口适配器模式。
再比如 Spring 的:
| 场景 | 适配器类 | 用途 |
|---|---|---|
| Spring 事件监听 | ApplicationListener + ApplicationListenerMethodAdapter | 避免每次自己判断类型 |
| MVC 拦截器 | HandlerInterceptorAdapter(旧版) | 让你只写 preHandle/afterCompletion |
| AOP 切点解析 | MethodInterceptorAdapter | 避免每种通知方式都要实现很多接口 |
最后一句总结
接口适配器模式是为了解决“接口方法太多,而实现类只需要部分方法”的问题。
它通过提供一个抽象适配器类,空实现所有方法,子类只重写需要的部分即可。
二、java 8新特性:接口的默认实现
2-1. 以前的接口有什么问题?(Java 8 之前)
Java 8 之前 接口里只能写方法声明,不能写方法实现:
public interface A {void test();
}
一个接口一旦被很多类实现,你想给接口里新增一个方法,所有实现类都会编译报错,因为它们没实现这个新方法。
这让接口拓展非常痛苦,改一个接口,所有实现类都要改。
2-2. Java 8 是怎么解决的?—— default 方法
Java 8 允许接口中写方法实现,只要加 default 关键字:
public interface A {default void test() {System.out.println("我是接口里的默认实现");}
}
这样:
实现类 可以不用重写 test()
如果实现类想改,也可以自己重写
这叫 接口默认方法(Default Method)
2-3. 示例代码
定义接口
public interface UserService {// 默认实现default void login() {System.out.println("默认登录逻辑:记录日志 + 输出 登录成功");}
}
实现类
public class UserServiceImpl implements UserService {// 不写 login() 也不会报错
}
使用
public class Test {public static void main(String[] args){UserService userService = new UserServiceImpl();userService.login(); // ✅ 直接用接口里的实现}
}
运行结果:
默认登录逻辑:记录日志 + 输出 登录成功
接口直接给了实现类一个默认行为。
2-4. 实现类可以自己重写吗?——可以(覆盖)
public class UserServiceImpl implements UserService {@Overridepublic void login() {System.out.println("自定义登录逻辑");}
}
输出变为:
自定义登录逻辑
2-5. 为什么要有 default 方法?
为了不破坏已有实现类,让接口可以“向后兼容” + “添加新能力”。
比如 Java 8 给 List 接口新增了 forEach() 方法,就是用的 default:
default void forEach(Consumer<? super T> action) {Objects.requireNonNull(action);for (T t : this) {action.accept(t);}
}
如果没有 default
→ List 的所有实现类(ArrayList, LinkedList, …)都要改
→ 不现实
2-6. 和 abstract 的区别?
| 关键字 | 出现位置 | 是否有方法体 | 实现类需不需要实现 |
|---|---|---|---|
abstract | 抽象类或接口 | ❌ 没有方法体 | ✅ 必须重写 |
default | 接口 | ✅ 有方法体 | ❌ 可选重写 |
2-7. 和 static 方法的区别(也出现在 Java 8 接口中)
public interface A {static void tool() { System.out.println("工具方法"); }
}
| 类型 | 调用方式 |
|---|---|
| default 方法 | 对象.default方法() |
| static 方法 | 接口名.static方法() |
2-8. 两个接口提供同名 default 方法怎么办?
如果一个类实现了两个接口,而这两个接口有同名 default 方法,则必须重写:
interface A { default void hello(){ System.out.println("A"); } }
interface B { default void hello(){ System.out.println("B"); } }class C implements A, B {@Overridepublic void hello() {A.super.hello(); // 指定调用哪个}
}
最终一句话总结
Java 8 允许接口通过 default 方法带实现,目的是让接口“可扩展不破坏旧代码”。
实现类可以直接用,也可以选择重写。
三、二者之间的关系
Java 8 的接口默认方法(default)其实就是为了解决“接口适配器模式”里必须写适配器类的问题。
3-1、问题回顾
接口适配器模式要解决的是这个问题:
接口方法太多,类只想实现其中几个方法。
传统解决方式:
接口 → 适配器类(空实现所有方法)→ 你的类继承适配器类
1、Java 8 之前(没有 default 的年代):必须写“接口适配器类”
比如:
public interface Listener {void onStart();void onRun();void onStop();
}public abstract class ListenerAdapter implements Listener {@Override public void onStart() {}@Override public void onRun() {}@Override public void onStop() {}
}public class A extends ListenerAdapter {@Override public void onRun() {System.out.println("只关心 onRun");}
}
你必须创建这个 Adapter 类,来帮你把不关心的方法空实现。
2、Java 8 出现 default 方法之后
接口可以直接写默认实现:
public interface Listener {default void onStart() {}default void onRun() {}default void onStop() {}
}
然后你的类 可以直接实现接口:
public class A implements Listener {@Overridepublic void onRun() {System.out.println("只关心 onRun");}
}
不需要再写 Adapter 类了!!!
3、直接对比
| 时代 | 怎么解决方法太多的问题 | 是否需要写“适配器类” |
|---|---|---|
| Java 8 之前 | 写一个抽象适配器类,空实现所有方法 | ✅ 需要 |
| Java 8 之后 | 接口中直接用 default 提供默认实现 | ❌ 不需要 |
3-2、一句话总结两者关系
Java 8 的接口 default 方法,就是对“接口适配器模式”的语言级支持。
它让你不再需要专门写适配器类。
换句话说:
default 方法 = 把适配器类“搬回接口内部”了。
