继承在 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.ancestors
和 B.metaclass.ancestors
会是什么样子?方法say_hello
存储在一个特征类中(我假设B.class
继承自),但它在哪里?
既然有两个祖先链(B.ancestors
和 B.class.ancestors
或 B.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 中是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章