Ruby 呼唤;超级";具有模块和继承的关键字

Ruby 呼唤;超级";具有模块和继承的关键字,ruby,inheritance,module,mixins,super,Ruby,Inheritance,Module,Mixins,Super,我认为将模块作为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 模块A def废话 如果定义为超级?(超级) 放上

我认为将模块作为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
模块A
def废话
如果定义为超级?(超级)
放上“你好,世界!”
结束
结束
X类
包括
结束
Y类
我原以为“y”会调用它的super blah()(因为它包含在类X中?),但我得到了:

test.rb:3:in blah:super:no超类方法'blah'


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

在对象上调用方法时,对于对象的类,查找响应该方法的祖先类或模块。当您在该方法中调用
super
时,您实际上是在沿着
祖先的树继续前进,寻找下一个响应相同方法名称的对象

X
Y
类的祖先树如下所示:

p X.ancestors #=> [ X, A, Object, Kernel, BaseObject ]
p Y.ancestors #=> [ Y, X, A, Object, Kernel, BaseObject ]
问题在于,在子类中再次插入模块不会在父类链中插入模块的第二个副本

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

如果模块
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, A, Object, Kernel, BaseObject ]
注意它是
C,B,A
,而不是
C,A,B,A

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


有一些实验证明了这种行为的不同方面。第一个是向对象添加
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
第三个实验使用
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, 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)
需要“撬动”
模块A
def废话
放“A::废话”
开始
超级的
营救
放“没有超级”
结束
结束
结束
X类
预加A
结束
Y类
非常有趣且有教育意义。你在开场白的开头说“我不能完全解释”是什么意思?@CarySwoveland我的意思是这个答案依赖于似乎支持这个假设的假设和实验,而不是权威性的解释和支持文档的链接。@CarySwoveland无论如何,我稍微更新了措辞。这可能是您所寻找的有关方法查找的参考的一部分:
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)