Ruby中元类的Base和Derived类之间的关系是什么?

时间:2010-01-26 06:41:57

标签: ruby metaprogramming metaclass

在Ruby中,我们可以在单例方法中使用super来调用相应的超类的单例方法,如下面的代码所示。

class Base
  def self.class_method
    puts "Base class method"
  end
end

class Derived < Base
  def self.class_method
    puts "Derived class method"
    super
  end
end

Derived.class_method
# Derived class method
# Base class method

但是,我似乎并不了解super中对Derived.class_method的调用是如何达到Base.class_method的。我假设class_method是在他们的元类上定义的,这是否意味着他们的元类有父/子关系? (我无法通过实验证实)

更新:我问的是这个问题,因为我记得看到某个地方在基础和派生类'元类之间存在某种关系(但我找不到它)更多)。除了知道实际super如何工作之外,我还想确认两个元类是否完全分开。

3 个答案:

答案 0 :(得分:11)

这里有四个类对象:

<Class>---class---><Class>
Base               #Base
   ^                  ^
   |                  |
   |                  |
 super              super
   |                  |
   |                  |
<Class>            <Class>
Derived---class--->#Derived

命名法:

  • &LT; ...&GT;是每个对象的类。
  • 班级名称在第二行。
  • 如果名称以#开头,那就是本征类(也就是单身类)。
  • 超级指向一个班级的超类
  • 课程指向班级。

当您调用Derived.class_method时,Ruby遵循“正确的一个然后向上”规则:首先转到对象的类,然后按照超类链,在找到方法时停止:

  • “class_method”调用的接收者是Derived。因此,请遵循Derived的类对象的链右,这是它的特征类(#Derived)。
  • Derived没有定义方法,所以Ruby跟链链接到#Derived的超类,即#Base。

  • 该方法在那里找到,因此Ruby将消息分派给#Base.class_method

你认为我不知道所有这些东西是不是我的头脑,是吗?在这里,我的大脑得到了所有这些元juju:Metaprogramming Ruby

第2部分。如何使“特征类”(又名“单身类”)隐藏起来

class Object
  def eigenclass
    class << self
      self
    end
  end
end

此方法将返回任何对象的本征类。现在,课程怎么样?这些也是对象。

p Derived.eigenclass               # => #<Class:Derived>
p Derived.eigenclass.superclass    # => #<Class:Base>
p Base.eigenclass                  # => #<Class:Base>

注意:上面是Ruby1.9。在Ruby 1.8下运行时,你会感到惊讶:

p Derived.eigenclass.superclass    # => #<Class:Class>

答案 1 :(得分:4)

为了澄清和纠正我在关于Ruby隐藏/暴露本征类的方式的评论中所写的内容,情况如下:

Ruby 1.8:

(1)Object#class方法总是返回对象的实际类的第一个真实(非本征类或iclass)超类。 例如

o = Object.new
class << o; end
o.class #=> returns Object, even though the _actual_ class is the eigenclass of o

换句话说,Object#class方法永远不会返回特征类,它会传递给它们 而是返回它在继承层次结构中找到的第一个“真正的”类。

(2)Class#superclass方法有两种情况。如果接收者不是本征类,那么它只返回超类。但是,如果接收器一个本征类,那么这个方法返回接收器的实际(即不一定是真实的)类。

# case where receiver is a normal class (i.e not an eigenclass)
Module.superclass #=> Object (behaves as expected)

# case where receiver is an eigenclass
class << Module; superclass; end #=> returns Class, this is NOT the superclass

从上面看,Class#superclass在正常类的情况下表现如预期,但对于本征类示例,它表明Module的本征类的超类是Class,这是不正确的。从这个图http://banisterfiend.wordpress.com/2008/10/25/the-secret-life-of-singletons/我们知道,Module的本征类的超类实际上是Object的本征类。我不确定为什么Ruby 1.8会有这种奇怪的行为。

Ruby 1.9:

(1)Object#class方法的行为与1.8版本相同。

(2)Class#superclass方法不再有两种情况,它现在以与处理普通类相同的方式处理本征类,并按预期返回实际的超类。

e.g

class << Module; superclass; end #=> #<Class:Object>

答案 2 :(得分:3)