java-接口适配器模式 & jsk8 接口默认实现

一、接口适配器模式

1-1. 问题背景

有时候,一个接口里可能有很多方法,比如:

java 复制代码
public interface Listener {
    void onStart();
    void onRun();
    void onStop();
    void onError();
    void onTimeout();
    // ... 假设有100个方法
}

但是现在类 A **只想用其中 1~2 个方法,**如果直接实现接口:

java 复制代码
class A implements Listener {
    @Override
    public void onStart() {}
    @Override
    public void onRun() {}
    @Override
    public void onStop() {}
    @Override
    public void onError() {}
    @Override
    public void onTimeout() {}
}

你会发现:

你必须把所有方法都实现一遍,即使大部分不需要。

非常麻烦。


1-2. 解决办法:接口适配器(Adapter Class)

一个类 来实现接口,并把所有方法写成空实现 :------适配器类

java 复制代码
public abstract class ListenerAdapter implements Listener {
    public void onStart() {}
    public void onRun() {}
    public void onStop() {}
    public void onError() {}
    public void onTimeout() {}
}

然后 A 不再直接实现接口,而是 继承这个适配器类

java 复制代码
class A extends ListenerAdapter {
    @Override
    public void onRun() {
        System.out.println("A 自己只关心 onRun");
    }
}

这样:

  • A 只需要重写自己关心的方法

  • 不用理会其他 99 个方法

  • 简化实现


1-3. 这种模式叫什么?

接口适配器模式(Interface Adapter Pattern)

又叫:

  • 缺省适配器

  • 默认适配器

  • 抽象适配器模式

本质:用一个抽象类"兜底",替你把多余的方法空实现掉。


1-4. Spring 里也大量使用这种适配器!

比如你写过:Servlet API:

java 复制代码
public class MyServlet extends HttpServlet {
    @Override
    protected 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 之前 接口里只能写方法声明,不能写方法实现

java 复制代码
public interface A {
    void test();
}

一个接口一旦被很多类实现,你想给接口里新增一个方法 ,所有实现类都会编译报错,因为它们没实现这个新方法

这让接口拓展非常痛苦,改一个接口,所有实现类都要改。


2-2. Java 8 是怎么解决的?------ default 方法

Java 8 允许接口中写方法实现 ,只要加 default 关键字:

java 复制代码
public interface A {
    default void test() {
        System.out.println("我是接口里的默认实现");
    }
}

这样:

  • 实现类 可以不用重写 test()

  • 如果实现类想改,也可以自己重写

这叫 接口默认方法(Default Method)


2-3. 示例代码

定义接口

java 复制代码
public interface UserService {
    // 默认实现
    default void login() {
        System.out.println("默认登录逻辑:记录日志 + 输出 登录成功");
    }
}

实现类

java 复制代码
public class UserServiceImpl implements UserService {
    // 不写 login() 也不会报错
}

使用

java 复制代码
public class Test {
    public static void main(String[] args){
        UserService userService = new UserServiceImpl();
        userService.login(); // ✅ 直接用接口里的实现
    }
}

运行结果:

复制代码
默认登录逻辑:记录日志 + 输出 登录成功

接口直接给了实现类一个默认行为


2-4. 实现类可以自己重写吗?------可以(覆盖)

java 复制代码
public class UserServiceImpl implements UserService {
    @Override
    public void login() {
        System.out.println("自定义登录逻辑");
    }
}

输出变为:

复制代码
自定义登录逻辑

2-5. 为什么要有 default 方法?

为了不破坏已有实现类,让接口可以"向后兼容" + "添加新能力"。

比如 Java 8 给 List 接口新增了 forEach() 方法,就是用的 default:

java 复制代码
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 接口中)

java 复制代码
public interface A {
    static void tool() { System.out.println("工具方法"); }
}
类型 调用方式
default 方法 对象.default方法()
static 方法 接口名.static方法()

2-8. 两个接口提供同名 default 方法怎么办?

如果一个类实现了两个接口,而这两个接口有同名 default 方法,则必须重写

java 复制代码
interface A { default void hello(){ System.out.println("A"); } }
interface B { default void hello(){ System.out.println("B"); } }

class C implements A, B {
    @Override
    public void hello() {
        A.super.hello(); // 指定调用哪个
    }
}

最终一句话总结

Java 8 允许接口通过 default 方法带实现,目的是让接口"可扩展不破坏旧代码"。
实现类可以直接用,也可以选择重写。


三、二者之间的关系

Java 8 的接口默认方法(default)其实就是为了解决"接口适配器模式"里必须写适配器类的问题。


3-1、问题回顾

接口适配器模式要解决的是这个问题:

接口方法太多,类只想实现其中几个方法。

传统解决方式:

复制代码
接口 → 适配器类(空实现所有方法)→ 你的类继承适配器类

1、Java 8 之前(没有 default 的年代):必须写"接口适配器类"

比如:

java 复制代码
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 方法之后

接口可以直接写默认实现:

java 复制代码
public interface Listener {

    default void onStart() {}
    default void onRun() {}
    default void onStop() {}
}

然后你的类 可以直接实现接口

java 复制代码
public class A implements Listener {
    @Override
    public void onRun() {
        System.out.println("只关心 onRun");
    }
}

不需要再写 Adapter 类了!!!


3、直接对比

时代 怎么解决方法太多的问题 是否需要写"适配器类"
Java 8 之前 写一个抽象适配器类,空实现所有方法 ✅ 需要
Java 8 之后 接口中直接用 default 提供默认实现 ❌ 不需要

3-2、一句话总结两者关系

Java 8 的接口 default 方法,就是对"接口适配器模式"的语言级支持。
它让你不再需要专门写适配器类。

换句话说:

default 方法 = 把适配器类"搬回接口内部"了。

相关推荐
鬼火儿7 小时前
网卡驱动架构以及源码分析
java·后端
老华带你飞8 小时前
房屋租赁|房屋出租|房屋租赁系统|基于Springboot的房屋租赁系统设计与实现(源码+数据库+文档)
java·数据库·spring boot·vue·论文·毕设·房屋租赁系统
TDengine (老段)8 小时前
TDengine 数学函数 ASCII 用户手册
java·大数据·数据库·物联网·时序数据库·tdengine·涛思数据
123461618 小时前
互联网大厂Java面试:从Spring Boot到微服务的探索
java·数据库·spring boot·微服务·面试·mybatis·orm
光仔December8 小时前
【Elasticsearch入门到落地】18、Elasticsearch实战:Java API详解高亮、排序与分页
java·elasticsearch·es排序·es分页·es高亮
码上零乱8 小时前
跟着小码学算法Day19:路径总和
java·数据结构·算法
ai旅人8 小时前
深入理解OkHttp超时机制:连接、读写、调用超时全面解析
java·网络·okhttp
NON-JUDGMENTAL8 小时前
Tomcat 配置问题速查表
java·tomcat
一 乐8 小时前
农产品销售系统|农产品电商|基于SprinBoot+vue的农产品销售系统(源码+数据库+文档)
java·javascript·数据库·vue.js·spring boot·后端·农产品销售系统