Методы модуля ядра Ruby, используемые в качестве методов класса объекта

Скажем, я хочу пропатчить модуль 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, что не должно допускать такого поведения. Но это так. Кто-нибудь знает, почему?

Спасибо


person kstratis    schedule 18.10.2017    source источник
comment
Это очень просто: Object — это Class, которое является Object. Вот почему он отвечает на say_hello :)   -  person Sergio Tulentsev    schedule 18.10.2017


Ответы (1)


Kernel просто включен в Object [...]

Это правильно.

[...] который не должен допускать такого поведения.

Вы упускаете из виду, что классы тоже являются объектами.


Давайте посмотрим, откуда берется метод say_hello, если obj является экземпляром Object:

obj = Object.new

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

Как и ожидалось. obj является экземпляром Object, а 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>

На этот раз obj является экземпляром Class, а Class также включает Kernel:

obj.class.include? Kernel
#=> true

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

В документации Ruby отмечается, что методы класса на самом деле просто методы экземпляра, определенные для объекта класса: (выделение добавлено)

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

Однако это просто частный случай большей синтаксической мощи Ruby, возможности добавлять методы к любому объекту. Классы — это объекты, поэтому добавление методов класса — это просто добавление методов к объекту класса.

person Stefan    schedule 18.10.2017