Ruby Kernel模块方法用作Object类方法

时间:2017-10-18 09:14:19

标签: ruby metaprogramming object-model

假设我想用我刚刚提出的一种方法修补Kernel模块:

module Kernel
  def say_hello
    puts "hello world"
  end
end

我现在可以做到这一点:

Object.new.say_hello # => hello world

但我也可以做以下通常不应该做的事情:

Object.say_hello # => hello world

由于Object包含Kernel,因此它采用其实例方法,因此所有Object个实例都应响应say_hello。到目前为止一切都很好。

然而,Object.say_hello似乎是一种类方法,只有在我们做了类似的事情时才能证明是合理的:

class << Object
  def say_hello
    puts "hello world"
  end  
end

say_hello存储在Object的单例类中会允许我们将其用作类方法,而只是Kernel中包含Object而不应该包含g #39; t允许这种行为。但确实如此。有人知道为什么?

由于

1 个答案:

答案 0 :(得分:5)

  

Kernel仅包含在Object [...]

这是正确的。

  

[...]不应该允许这种行为。

你忽略了类也是对象。

如果say_helloobj的一个实例,让我们看看Object方法的来源:

obj = Object.new

obj.method(:say_hello)
#=> #<Method: Object(Kernel)#say_hello>

正如预期的那样。 objObject的实例,Object包含Kernel

obj.class.include? Kernel
#=> true

obj.class.ancestors
#=> [Object, Kernel, BasicObject]

现在让我们看看如果obj是类Object会发生什么:

obj = Object

obj.method(:say_hello)
#=> #<Method: Class(Kernel)#say_hello>

此时objClass的实例,Class还包含Kernel

obj.class.include? Kernel
#=> true

obj.class.ancestors
#=> [Class, Module, Object, Kernel, BasicObject]

Ruby的documentation注意到类方法实际上只是在类对象上定义的实例方法:(强调添加)

class C
  def self.my_method
    # ...
  end
end
     

然而,这只是Ruby中更强大的语法能力的一种特殊情况,即向任何对象添加方法的能力。类是对象,因此添加类方法只是向Class对象添加方法