← 返回题目列表

类适配器和对象适配器有什么区别?

高频 中等 第 3 / 25 题 更新于 2026/07/28
适配器模式类适配器对象适配器继承组合

简化版

类适配器通过继承被适配者并实现目标接口来适配,对象适配器通过组合持有被适配者来适配。类适配器依赖继承,耦合更强且受语言单继承限制;对象适配器依赖组合,更灵活,工程中更常用。

详细版

类适配器的结构是:

Adapter extends Adaptee implements Target

它直接继承被适配者,所以可以复用父类方法,也可以重写部分行为。但在 Java 这类单继承语言中,如果 Adapter 已经继承了 Adaptee,就不能再继承其他类。

对象适配器的结构是:

Adapter implements Target
Adapter has-a Adaptee

它通过成员变量持有被适配者,把目标接口调用委托给被适配者。对象适配器更符合组合优于继承的原则,也更容易替换不同 Adaptee。

面试建议回答:类适配器适合被适配者稳定、需要复用或改写父类行为的场景;对象适配器适合集成第三方对象、多实现切换、运行时替换等场景,真实项目更推荐对象适配器。

完整版教学

一、类适配器靠继承完成转换

类适配器的写法大概是:

interface Target {
    void request();
}

class Adaptee {
    void specificRequest() {
        System.out.println("已有能力");
    }
}

class ClassAdapter extends Adaptee implements Target {
    public void request() {
        specificRequest();
    }
}

客户端调用 Target.request(),实际执行的是 Adaptee.specificRequest()

这种方式简单直接,但 Adapter 和 Adaptee 形成了继承关系。

二、对象适配器靠组合完成转换

对象适配器写法是:

class ObjectAdapter implements Target {
    private final Adaptee adaptee;

    ObjectAdapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    public void request() {
        adaptee.specificRequest();
    }
}

这种方式不继承 Adaptee,而是持有一个 Adaptee 对象。

好处是:只要对象实现了需要的能力,就可以传进来。适配器不和某个具体父类强绑定。

三、继承和组合带来的差异

类适配器的特点:

  • 能直接使用父类方法;
  • 可以重写父类部分行为;
  • 对 Adaptee 耦合较强;
  • 受单继承限制;
  • 不方便运行时替换被适配者。

对象适配器的特点:

  • 通过组合复用已有对象;
  • 可以适配 Adaptee 的子类或不同实现;
  • 更容易依赖注入;
  • 运行时可以替换被适配对象;
  • 更符合组合优于继承。

四、为什么工程中更常用对象适配器

真实项目中,被适配者经常是第三方 SDK 客户端、远程服务代理或已有组件。

这些对象通常由 Spring 容器、工厂或配置创建。适配器只需要把它注入进来:

@Component
class WechatPayAdapter implements PayService {
    private final WechatPayClient client;

    WechatPayAdapter(WechatPayClient client) {
        this.client = client;
    }
}

这种方式更符合依赖注入和单元测试习惯。测试时可以传入 mock 的 client,不需要真的继承第三方类。

五、类适配器也不是完全没用

如果被适配者是一个稳定基类,并且你确实需要重写它的部分行为,类适配器也有价值。

比如某些老系统基类已经提供了大量受保护方法,适配器继承它可以比较方便地复用内部能力。

但这类场景要谨慎,因为继承会让 Adapter 暴露在 Adaptee 的变化影响下。父类一变,适配器可能被动受到影响。

六、面试时怎么回答更稳

可以按这个逻辑答:

  1. 类适配器用继承;
  2. 对象适配器用组合;
  3. 类适配器耦合强,受单继承限制;
  4. 对象适配器灵活,可替换,可注入;
  5. 工程中更推荐对象适配器。

这比只背“一个继承一个组合”更完整。

七、继承适配与组合适配的约束

对象适配器可以在运行时接收 3 个不同 Adaptee 实现;类适配器编译期只能继承选定父类,Java 还受单继承限制。

class adapter: Adapter extends Adaptee; object adapter: Adapter -> adaptee field

适配器的验收标准不是“调用成功”,而是 Target 的业务语义在转换前后保持成立。参数、单位、时区、精度、空值、错误码和资源所有权都应逐项形成契约样例,否则最危险的错误会以“成功响应”的形式潜伏。

八、语义边界与契约样例验证

检查维度应确认的内容
机制正确性选择依据是复用方式和替换需求,不是简单背“对象一定优于类”。
适用边界类适配器可覆写受保护行为但耦合更强;对象适配器更易替换、测试和组合,工程中通常优先。
测试证据对同一业务样例验证转换前后的单位、精度、空值和异常语义,并覆盖不可逆转换
工程代价重点统计字段映射与对象分配成本、外部模型变更影响面,以及多层版本适配造成的认知债务

易错点:选择依据是复用方式和替换需求,不是简单背“对象一定优于类”。

九、常见误区与追问

  • 误区:类适配器在 Java 中依靠多重类继承。 Java 不支持多重类继承,通常通过继承 Adaptee、实现 Target 来完成。
  • 误区:适配器只需要让方法签名能够编译。 真正的适配还包括单位、时区、错误码、空值、资源释放和幂等语义;签名一致不代表行为兼容。
  • 追问:什么时候类适配器仍有价值? 需要利用 protected 扩展点且父类稳定、不会占用其他继承需求时可以考虑。
  • 追问:适配器应该放在哪一层? 通常放在系统边界或防腐层,让业务层只依赖 Target,避免第三方模型和异常向内扩散。
  • 追问:适配器测试为什么需要契约样例? 因为类型相同仍可能单位、精度或错误语义不同;契约样例能证明转换后的行为而不只是编译通过。

十、加强记忆

类适配器记成“继承旧接口,实现新接口”,对象适配器记成“实现新接口,持有旧对象”。类适配器贴得近,能改父类行为但耦合强;对象适配器隔得开,组合灵活,工程里更常用。