atsushi_1995
@atsushi_1995

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Ruby 「putsがクラスメソッドの中で使用できる理由」

解決したいこと

クラスメソッドの中で「puts」が使える理由がわかりません。

リファレンスにある、Rubyの「ObjectクラスはKernelモジュールをincludeしている」より、インスタンスメソッドの中で使える理由はわかるのですが、クラスメソッドの中でも使える理由がわかりません。

現時点の認識として
・includeしたモジュールのメソッドはインスタンスメソッドとして継承と同じように使える
・extendしたモジュールのメソッドはクラスメソッドとして継承と同じように使える(厳密には特異メソッドというべきかもしれないですが)
という認識を持っています。
その為、「ObjectクラスはKernelモジュールをincludeに加えてextendもする必要がある」のではないかと考えております。

仮説1

Kernelモジュールの中で、「module_function :puts」でモジュール関数としてputsを指定することで、クラスメソッドの中で「Kernel.puts」を呼び出している。
しかし、クラスメソッドの中で「self.puts」でも呼び出せるのはなぜでしょうか?
この場合のselfは「MyClass」となりエラーにならないのでしょうか?

class MyClass
  def self.class_method
    self.puts("練習")
  end
end

MyClass.class_method #=>練習

仮説2

puts以外のケースで試した場合にはエラーになる為、モジュール関数というよりもObjectクラス自体に何か特殊な仕掛けがあるのではないかと思いました。
例えば、ObjectクラスへのincludeはObjectクラスの特異クラスへのincludeも兼ねるなど。

module MyModule
  def instance_method
    puts "This is an instance method from MyModule"
  end
  module_function :instance_method
end

class MyClass
  include MyModule
  def self.class_method
    instance_method
  end
end

MyClass.class_method #=>エラー

参照にしたリファレンス

0

2Answer

Ruby では通常のクラスオブジェクトは Class のインスタンスでもあり、かつ ClassObject のサブクラスです。つまり MyClass.is_a?(Object) == true になります。

よって MyClass クラスオブジェクトは Object から継承した puts インスタンスメソッドを持っています。 MyClass のクラスメソッド内における self.puts はこのインスタンスメソッドの呼び出しになります。

Object の任意のインスタンスメソッドが MyClass のクラスメソッド内で呼べることは以下のコードで確認できます。

class Object
  def my_method
    'ok'
  end
end

class MyClass
  def self.my_class_method
    my_method
  end
end

puts MyClass.my_class_method # => ok

# 余談だがこういうことである
puts MyClass.my_method # => ok
puts MyClass.new.my_method # => ok
1Like

Comments

  1. @atsushi_1995

    Questioner

    @uasi
    ご回答ありがとうございます!

    ・クラスは「Classクラスのインスタンス」であると同時に「Objectクラスのインスタンス」でもある
    ・my_methodは「Object.new」でインスタンス化された「MyClass」のインスタンスメソッド
    ・Objectクラスのインスタンスメソッドに関してはObjectクラスの特異クラスに定義する必要なく、各クラスの中でクラスメソッドとして呼び出すことができる。
    ということでしょうか?!

    最後の文について、以下に自分のイメージを補足しました!

    class Object
      def my_method
        'ok'
      end
    end
    
    class MyClass
      def self.my_class_method #=>クラスメソッド(特異メソッド)
        Object.new.my_method #=>インスタンスメソッド
        MyClass.my_method #=>同じ
        self.my_method #=>同じ
      end
    end
    
  2. ・クラスは「Classクラスのインスタンス」であると同時に「Objectクラスのインスタンス」でもある

    考え方としてはそうです。厳密にいうと後ろ半分は『継承の性質により「Objectクラスのインスタンス」のようにも振る舞う』です。
    前半分の性質は MyClass.instance_of?(Class) == true により、
    後ろ半分は MyClass.instance_of?(Object) == falseMyClass.is_a?(Object) == true により確かめられます。

    ・my_methodは「Object.new」でインスタンス化された「MyClass」のインスタンスメソッド

    「インスタンスメソッド」が「MyClassClass クラスのインスタンスとして見たとき、持っているメソッド」という意味ならこの文は合っています。(『「MyClass」のインスタンスメソッド』という言い方は普通「MyClass クラスから作ったインスタンスが持っているメソッド」を指しますが、今回はそれではありません。)

    ・Objectクラスのインスタンスメソッドに関してはObjectクラスの特異クラスに定義する必要なく、各クラスの中でクラスメソッドとして呼び出すことができる。

    合っています。

  3. Ruby のオブジェクト指向システムについて勉強していらっしゃるようなのでもう少し補足すると、上記項目の1つ目の「AB のサブクラスであるとき、 A のインスタンスは B のインスタンスのようにも振る舞う」という性質は一般的なオブジェクト指向言語に当てはまります。2つ目と3つ目は Ruby 特有です。

  4. @atsushi_1995

    Questioner

    @uasi
    ご返信ありがとうございます!

    何度もすみません、
    『継承の性質により「Objectクラスのインスタンス」のようにも振る舞う』
    の部分だけ再度お聞きしたいです。

    作成したクラスは「Classクラスのインスタンス」であるということから、Classクラスで定義したインスタンスメソッドに対してクラスメソッドの中でも使えることは理解できました。
    しかし、「厳密にいうとObjectクラスのインスタンスではない」が「Objectクラスのインスタンスメソッドをクラスメソッドの中で使うことができる」というのがわからないです。
    『継承の関係で「Objectクラスのインスタンス」のようにも振る舞う』ことができるというのは、「Objectクラスは作成したクラスがClassクラスのインスタンスであるという性質も継承している」ということでしょうか?

    通常のクラスの継承の場合には下記の場合にparent_methodを呼び出せないですが

    class Parent < Object
      def parent_method
        'ok'
      end
    end
    class Child < Parent
      def self.my_class_method
        parent_method
      end
    end
    puts Child.my_class_method # => エラー
    

    継承で「A が B のサブクラスであるとき、 A のインスタンスは B のインスタンスのようにも振る舞う」すなわち「サブクラスはClassクラスのインスタンスであるという性質も継承」できる場合には、上記の場合でもいけるのではないかと思ってしまいます。
    Objectクラスが継承する場合は特別ということでしょうか??

    class Object
      def parent_method
        'ok'
      end
    end
    
    class Child < Object
      def self.my_class_method
        parent_method
      end
    end
    
    puts Child.my_class_method # => ok
    

    個人的な仮説としては
    rubyの継承の形が
    BasicObject > Kernel > Object > Module > Class
    ではなく実際には
    BasicObject > Kernel > Object > Module > Class > BasicObject > Kernel > Object > Module > Class >
    のように循環しているのが関係しているのかと思っているのですが、わからないです。

    長文になり申し訳ございません。

『継承の関係で「Objectクラスのインスタンス」のようにも振る舞う』ことができるというのは、「Objectクラスは作成したクラスがClassクラスのインスタンスであるという性質も継承している」ということでしょうか?

これは逆で、 Class クラスは祖先である Object クラスから性質を受け継いでいるということです。

Objectクラスが継承する場合は特別ということでしょうか??

子が親を継承するという言い方をするので、「Object クラスを継承する」が正しいです。そして Object クラスを継承する場合が特別というのはその通りで、仮説でお考えの内容で合っています。 Object クラスの祖先に Object クラスがいるという循環構造がミソです。

1Like

Comments

  1. @atsushi_1995

    Questioner

    @uasi
    ご返信ありがとうございます!

    やっと理解することができました:bow_tone1:
    何度もありがとうございました、すごくわかりやすかったです!

Your answer might help someone💌