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

kst*_*tis 3 ruby metaprogramming object-model

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

module Kernel
  def say_hello
    puts "hello world"
  end
end
Run Code Online (Sandbox Code Playgroud)

我现在可以做到这一点:

Object.new.say_hello # => hello world
Run Code Online (Sandbox Code Playgroud)

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

Object.say_hello # => hello world
Run Code Online (Sandbox Code Playgroud)

由于Object包含Kernel它的实例方法,因此所有Object实例都应该响应say_hello.到现在为止还挺好.

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

class << Object
  def say_hello
    puts "hello world"
  end  
end
Run Code Online (Sandbox Code Playgroud)

存储say_helloObject单例类中将允许我们将它用作类方法,而Kernel只是包含在Object其中,不应该允许这种行为.但确实如此.有人知道为什么?

谢谢

Ste*_*fan 5

Kernel仅包括在Object[...]

那是正确的.

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

你忽略了类也是对象.


say_hello如果obj是以下实例,让我们看看方法的来源Object:

obj = Object.new

obj.method(:say_hello)
#=> #<Method: Object(Kernel)#say_hello>
Run Code Online (Sandbox Code Playgroud)

正如预期的那样.obj是的一个实例ObjectObject包括Kernel:

obj.class.include? Kernel
#=> true

obj.class.ancestors
#=> [Object, Kernel, BasicObject]
Run Code Online (Sandbox Code Playgroud)

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

obj = Object

obj.method(:say_hello)
#=> #<Method: Class(Kernel)#say_hello>
Run Code Online (Sandbox Code Playgroud)

这一次,obj是一个实例Class,并Class还包括Kernel:

obj.class.include? Kernel
#=> true

obj.class.ancestors
#=> [Class, Module, Object, Kernel, BasicObject]
Run Code Online (Sandbox Code Playgroud)

Ruby的文档指出,类方法实际上只是在类对象上定义的实例方法:(强调添加)

class C
  def self.my_method
    # ...
  end
end
Run Code Online (Sandbox Code Playgroud)

但是,这只是Ruby中具有更强语法能力的特殊情况,即向任何对象添加方法的能力.类是对象,因此添加类方法只是向Class对象添加方法.