[英]Calling “super” keyword with modules and inheritance
I thought that including a module as a mixin in a class "added the functions" to the class. 我认为在类中包含一个模块作为mixin“将函数添加到”类中。
I do not understand why this does not work as expected: 我不明白为什么这不能按预期工作:
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
I was expecting "y" to call its super blah() (since its included in class X?) but instead i got: 我期待“y”调用它的超级blah()(因为它包含在X类中?)但我得到了:
test.rb:3:in blah: super: no superclass method `blah' test.rb:3:in blah:super:没有超类方法`blah'
You're running into nuances of Ruby's object hierarchy and how method lookups interact with included modules. 您正在遇到Ruby的对象层次结构的细微差别以及方法查找如何与包含的模块交互。
When you invoke a method on a object, Ruby walks over the ancestors
list for the object's class, looking for an ancestor class or module that responds to that method. 当您在对象上调用方法时, Ruby会遍历对象类的ancestors
列表 ,查找响应该方法的祖先类或模块。 When you invoke super
in that method, you're effectively continuing your walking up the tree of ancestors
, looking for the next object that responds to the same method name. 当您在该方法中调用super
时,您将有效地继续沿着ancestors
树行走,寻找响应相同方法名称的下一个对象。
The ancestor tree for your X
and Y
classes look like this: X
和Y
类的祖先树如下所示:
p X.ancestors #=> [ X, A, Object, Kernel, BaseObject ]
p Y.ancestors #=> [ Y, X, A, Object, Kernel, BaseObject ]
The problem is that include
ing the module a second time, in a child class, does not inject a second copy of the module in the ancestors chain. 问题是, include
荷兰国际集团该模块的第二次,在一个子类, 不注的祖先链模块的第二个副本。
Effectively what is happening is when you invoke Y.new.blah
, Ruby begins looking for a class that responds to blah
. 实际上发生的事情就是当你调用Y.new.blah
,Ruby开始寻找一个响应blah
的类。 It walks past Y
, and X
, and lands on A
which introduces the blah
method. 它走过Y
和X
,然后登陆A
,它引入了blah
方法。 When A#blah
invokes super
, the "pointer" into your ancestor list is already pointing at A
, and Ruby resumes looking from that point for another object responding to blah
, starting with Object
, Kernel
, and then BaseObject
. 当A#blah
调用super
,你的祖先列表中的“指针”已经指向A
,并且Ruby从该点继续寻找另一个响应blah
对象,从Object
, Kernel
,然后是BaseObject
。 None of these classes have a blah
method, so your super
invocation fails. 这些类都没有blah
方法,所以你的super
调用失败了。
A similar thing happens if a module A
includes a module B
, and then a class includes both module A
and B
. 如果模块A
包括模块B
,则类似的事情发生,然后类包括模块A
和B
The B
module is not included twice: B
模块不包括两次:
module A; end
module B; include A; end
class C
include A
include B
end
p C.ancestors # [ C, B, A, Object, Kernel, BaseObject ]
Note that it's C, B, A
, not C, A, B, A
. 注意它是C, B, A
,而不是C, A, B, A
。
The intent would seem to be to allow you to safely invoke super
inside any of A
's methods without worrying about how consuming class hierarchies may inadvertently include A
twice. 意图似乎是允许您在任何A
方法中安全地调用super
,而不必担心类层次结构可能无意中包含A
两次。
There are a few experiments that demonstrate different aspects of this behavior. 有一些实验证明了这种行为的不同方面。 The first is adding a blah
method to Object, which allows the super
call to pass: 第一个是向Object添加一个blah
方法,它允许super
调用传递:
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
The second experiment is to use two modules, BaseA
and A
, which does cause the modules to be inserted twice, correctly, in the ancestors
chain: 第二个实验是使用两个模块, BaseA
和A
,它确实使模块在ancestors
链中正确插入两次:
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, A, X, BaseA, Object, ...]
Y.new.blah
# Output
# A::blah
# BaseA::blah
A third experiement uses prepend
, instead of include
, which places the module in front of the object in the ancestors
hierarchy and interestingly does insert a duplicate copy of the module. 第三个实验使用prepend
而不是include
,它将模块放在 ancestors
层次结构中的对象前面 ,有趣的是插入模块的副本。 This allows us to reach the point where effectively Y::blah
invokes X::blah
, which fails because Object::blah
does not exist: 这允许我们达到有效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, A, X, Object, ... ]
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)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.