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会把模块插入到类的直接父类之前,后续混入的模块会排在更早混入的模块前面,方法查找时会优先匹配靠前的模块。
我们拆解代码执行的每一步:
- 初始状态:类C的祖先链是
[C, Object, Kernel, BasicObject],没有混入任何模块。 - 第一次
include A:A被插入到C和Object之间,祖先链变为[C, A, Object, Kernel, BasicObject]。此时调用print,方法查找先找C(没有),再找A,执行A的方法输出A。 - 接着
include B:B被插入到C和A之间,祖先链变为[C, B, A, Object, Kernel, BasicObject]。此时调用print会优先匹配B的方法,输出B。 - 再次
include A:Ruby检测到A已经在C的祖先链里,不会修改链结构。方法查找还是先找到B,所以输出B。
如果想让第三次调用输出A,可以改用prepend(把模块插入到类本身之前),不过这会改变方法查找的基础逻辑;或者通过操作祖先链移除已混入的模块再重新包含,但日常开发中很少这么做。
内容的提问来源于stack exchange,提问作者Alexander Presber
相关产品推荐
相关产品推荐

