我不明白为什么这不能按预期工作:
module A def blah super if defined?(super) puts "hello,world!" end end class X include A end class Y < X include A end y = Y.new y.blah
我期待“y”调用它的超级blah()(因为它包含在X类中?)但我得到了:
test.rb:3:in blah: super: no superclass method `blah’
解决方法
当您在对象上调用方法时,为对象的类调用Ruby walks over the ancestors
list,查找响应该方法的祖先类或模块.当您在该方法中调用super时,您将有效地继续沿着祖先树行走,寻找响应相同方法名称的下一个对象.
X和Y类的祖先树如下所示:
p X.ancestors #=> [ X,A,Object,Kernel,BaSEObject ] p Y.ancestors #=> [ Y,X,BaSEObject ]
问题是在子类中第二次包含模块不会在祖先链中注入模块的第二个副本.
实际上发生的事情就是当你调用Y.new.blah时,Ruby开始寻找一个响应blah的类.它走过Y和X,然后登陆A,它引入了blah方法.当A#blah调用super时,你的祖先列表中的“指针”已经指向A,并且Ruby从该点继续寻找另一个响应blah的对象,从Object,Kernel开始,然后是BaSEObject.这些类都没有blah方法,所以你的超级调用失败了.
如果模块A包含模块B,则类似的事情发生,然后类包括模块A和B.B模块不包括两次:
module A; end module B; include A; end class C include A include B end p C.ancestors # [ C,B,BaSEObject ]
注意它是C,而不是C,A.
意图似乎是允许您在A的任何方法中安全地调用super,而不必担心类层次结构可能无意中包含A两次.
有一些实验证明了这种行为的不同方面.第一个是向Object添加一个blah方法,它允许超级调用传递:
class Object; def blah; puts "Object::blah"; end; end module A def blah puts "A::blah" super end end class X include A end class Y < X include A end Y.new.blah # Output # A::blah # Object::blah
第二个实验是使用两个模块,BaseA和A,它确实使模块在祖先链中正确插入两次:
module BaseA def blah puts "BaseA::blah" end end module A def blah puts "A::blah" super end end class X include BaseA end class Y < X include A end p Y.ancestors # [ Y,BaseA,...] Y.new.blah # Output # A::blah # BaseA::blah
第三个实验使用prepend而不是include,它将模块放在祖先层次结构中的对象前面,有趣的是插入模块的副本.这允许我们达到有效Y :: blah调用X :: blah的地步,因为Object :: blah不存在而失败:
require 'pry' module A def blah puts "A::blah" begin super rescue puts "no super" end end end class X prepend A end class Y < X prepend A end p Y.ancestors # [ A,Y,... ] Y.new.blah # Output # A::blah (from the A before Y) # A::blah (from the A before X) # no super (from the rescue clause in A::blah)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。