继承在 Ruby 中是如何工作的?

Posted

技术标签:

【中文标题】继承在 Ruby 中是如何工作的?【英文标题】:How does Inheritance work in Ruby? 【发布时间】:2012-12-18 16:28:30 【问题描述】:

根据his talk about the Ruby Object Model 中的 Dave Thomas 所说,Ruby 中没有“类方法”。只是方法的接收者是“类对象”还是“实例对象”的区别。

class Dave
  def InstaceMethod              ### will be stored in the current class (Dave)
    puts "Hi"
  end
  class << self                  ### Creates an eigenclass, if not created before
    def say_hello
      puts "Hello"
    end
  end
end

默认情况下,ancestors 方法不显示元类:

class Dave
  class << self
    def metaclass                ### A way to show the hidden eigenclass
      class << self; self; end
    end
  end
end

p Dave.ancestors
# => [Dave, Object, Kernel, BasicObject]
p Dave.metaclass.ancestors
# => [Class, Module, Object, Kernel, BasicObject]

但是,我认为真正的应该是这样的:

# => [<eigenclass>, Class, Module, Object, Kernel, BasicObject]

p Dave.class.instance_method(false)
# => [:allocate, :new, :superclass]
p Dave.metaclass.instance_method(false)
# => [:say_hello, :metaclass]

现在是继承。

class B < Dave
end

p B.say_hello
# => "Hello"
p B.ancestors
# => [B, Dave, Object, Kernel, BasicObject]
p B.class.instance_methods(false)
# => [:allocate, :new, :superclass]

以下将为B 创建一个新的特征类:

p B.metaclass.ancestors
# => [Class, Module, Object, Kernel, BasicObject]
p B.metaclass.instance_method(false)
# => []

    如果还包含特征类,B.ancestorsB.metaclass.ancestors 会是什么样子?方法say_hello 存储在一个特征类中(我假设B.class 继承自),但它在哪里?

    既然有两个祖先链(B.ancestorsB.class.ancestorsB.metaclass.ancestors),那么继承实际上是如何发生的?

【问题讨论】:

【参考方案1】:

一个对象(以及作为对象的类,Class 的实例)有一个指向其类的类字段。创建一个单例类(eigenclass/metaclass)会创建一个匿名类并将这个指针更改为指向匿名类,其类指针将指向原始类。 class 方法不显示匿名类,只显示原始类。对于 mixins 也是一样。一个类有一个超类字段。方法include 创建一个匿名代理,超类指针更改为指向匿名代理类,并从那里指向超类。方法ancestors 不显示匿名类,而是显示包含模块的名称。 superclass 方法不显示匿名代理类,只显示原始超类。

您可以阅读:Why are symbols in Ruby not thought of as a type of variable?

在对此答案的评论中,有一个关于单例类的有趣文章的链接,可以在 Devalot 博客上找到。

需要一些时间来吸收这些继承链。一张好图值得长篇大论,我推荐The Pickaxe的第24章元编程,里面有关于所有这些链的各种图片。

默认情况下,祖先方法不显示元类: 和 1. 当特征类也包括在内时,B.ancestors ... 会是什么样子?

ancestors 关注超类链。本征类不属于超类链。

p Dave.metaclass.ancestors => [类、模块、对象、内核、基本对象] 但是,我认为真正的应该是这样的: => [“eigenclass”,类,模块,对象,内核,BasicObject]

正确。

您可以简化您的 Dave 课程:

class Dave
    def self.say_hello    # another way to create an eigenclass, if not created before
      puts "Hello"
    end
    def self.metaclass    # A way to show the hidden eigenclass
        class << self
            self
        end
    end
end

Dave.say_hello           # => Hello
Dave.new.class.say_hello # => Hello
p Dave.metaclass.instance_methods(false) # => [:say_hello, :metaclass]
p Dave.singleton_methods                 # => [:say_hello, :metaclass]

def self.metaclass 从 Ruby 1.9.2 开始就多余了,它引入了Object#singleton_class

【讨论】:

【参考方案2】:

Eigenclass 是一个鬼鬼祟祟的隐藏类。你已经通过开课成功地揭示了它。但它并不存在于普通阶级的祖先中。而且由于它是隐藏的,因此您无法通过将ancestors 方法发送到特征类本身来查看它。继承树如下:

B ---S-->  Dave   ---S---> Object  ---S---> BasicObject
|            |               |                  |
E            E               E                  E
|            |               |                  |
#B --S--> #Dave   ---S---> #Object ---S---> #BasicObject --S---> Class,,Object,BasicObject

S 代表超类,E 代表本征类。

【讨论】:

以上是关于继承在 Ruby 中是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

ruby 我自己测试Ruby类,对象以及它们如何组合在一起,继承等等。

Ruby 继承和类型

从 Ruby 中的模块/mixins 继承类方法

ruby Ruby继承

从 Ruby 中的继承类调用方法

c++继承是如何工作的?