You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby 3.3.0多次include模块后实例方法未按预期覆盖的疑问

为什么重复include模块A后输出还是B?

先看你的代码:

module A
  def print
    puts "A"
  end
end

module B
  def print
    puts "B"
  end
end

class C
end

c_instance = C.new

C.send(:include, A)
c_instance.print

C.send(:include, B)
c_instance.print

C.send(:include, A)
c_instance.print

你的预期输出:

A
B
A

实际输出:

A
B
B

核心原因:Ruby不会重复包含已存在的模块,且后混入的模块优先级更高

Ruby处理模块混入有两个关键规则:

  • 模块仅会被混入一次:如果类的祖先链里已经存在该模块,再次调用include不会做任何修改。
  • 后混入的模块方法优先级更高:include会把模块插入到类的直接父类之前,后续混入的模块会排在更早混入的模块前面,方法查找时会优先匹配靠前的模块。

我们拆解代码执行的每一步:

  1. 初始状态:类C的祖先链是 [C, Object, Kernel, BasicObject],没有混入任何模块。
  2. 第一次include A:A被插入到C和Object之间,祖先链变为 [C, A, Object, Kernel, BasicObject]。此时调用print,方法查找先找C(没有),再找A,执行A的方法输出A。
  3. 接着include B:B被插入到C和A之间,祖先链变为 [C, B, A, Object, Kernel, BasicObject]。此时调用print会优先匹配B的方法,输出B。
  4. 再次include A:Ruby检测到A已经在C的祖先链里,不会修改链结构。方法查找还是先找到B,所以输出B。

如果想让第三次调用输出A,可以改用prepend(把模块插入到类本身之前),不过这会改变方法查找的基础逻辑;或者通过操作祖先链移除已混入的模块再重新包含,但日常开发中很少这么做。

内容的提问来源于stack exchange,提问作者Alexander Presber

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 20:52:10