gpt4 book ai didi

ruby - 使用模块和继承调用 "super"关键字

转载 作者:数据小太阳 更新时间:2023-10-29 07:24:26 24 4
gpt4 key购买 nike

我认为在类中包含一个模块作为 mixin 可以“为类添加功能”。

我不明白为什么这不能按预期工作:

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”会调用它的 super blah()(因为它包含在 X 类中?)但我却得到了:

test.rb:3:in blah: super: no superclass method `blah'

最佳答案

您遇到了 Ruby 对象层次结构的细微差别以及方法查找如何与包含的模块交互。

当你调用一个对象的方法时,Ruby walks over the ancestors list对于对象的类,寻找响应该方法的祖先类或模块。当您在该方法中调用 super 时,您实际上是在继续您在祖先树上行走,寻找下一个 响应相同方法名称的对象。

XY 类的祖先树如下所示:

p X.ancestors #=> [ X, A, Object, Kernel, BaseObject ]
p Y.ancestors #=> [ Y, X, A, Object, Kernel, BaseObject ]

问题在于,在子类中include模块第二次不会在祖先链中注入(inject)模块的第二个副本。 p>

实际上,当您调用 Y.new.blah 时,Ruby 开始寻找响应 blah 的类。它经过 YX,然后到达引入 blah 方法的 A。当 A#blah 调用 super 时,您祖先列表中的“指针”已经指向 A,Ruby 从该点继续寻找另一个响应 blah 的对象,从 ObjectKernel 开始,然后是 BaseObject。这些类都没有 blah 方法,因此您的 super 调用失败。

如果一个模块 A 包含一个模块 B,然后一个类同时包含模块 AB,就会发生类似的事情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 ]

注意是C, B, A,不是C, A, B, A

这样做的目的似乎是让您可以在 A 的任何方法中安全地调用 super,而不必担心消费类层次结构可能会无意中包含 A 两次。


有一些实验证明了这种行为的不同方面。第一个是为 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

第二个实验是使用两个模块,BaseAA确实导致模块被正确插入两次,在祖先链:

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

第三个实验使用prepend,而不是include,它将模块放在前面 ancestors 层次结构,有趣的是确实插入了模块的副本。这使我们能够达到有效地 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)

关于ruby - 使用模块和继承调用 "super"关键字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41451292/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com