メソッドの定義場所
メソッドがどこに定義されているのかはowner
メソッドを使うことによって確認できます。
class User < ApplicationRecord
def self.from_class
return "from class"
end
def from_instance
return "from instance"
end
end
上記のようなクラスを想定します。
pry(main)> User.instance_method(:from_instance).owner
# => User(...)
pry(main)> User.method(:from_class).owner
# => #<Class:User(...)>
pry(main)> User.singleton_class
# => #<Class:User(...)>
インスタンスメソッドはUserクラスに定義されている一方で、クラスメソッドはsingleton_classに定義されています。
pry(main)> user = User.last
pry(main)> user.class
# => User(...)
pry(main)> User.class
# => Class
userインスタンス
の所属先はUserクラス
、Userクラス
の所属先はClassクラス
(UserクラスはClassクラスのインスタンス)であるのに、なぜクラスメソッドはsingleton_classに定義されているのでしょうか。
singleton_classとは
↑ こちらの記事を読んでいただければ僕の記事は読まなくても大丈夫です。
singleton
= 特異
特異メソッド(singleton_method)はクラスメソッドのような、特定のオブジェクトのみが持つメソッドです。
同様に考えると、特異クラス(singleton_class)は、特定のオブジェクトのみが持つクラスです。
インスタンスメソッドが呼び出された場合、そのインスタンスの所属先のクラス内でメソッドを探索します。
この理論でいくと、クラスメソッドが呼び出された場合は、所属先であるClassクラス内でメソッドを探索するはずです。
しかし、Classクラスがあらゆるクラスの所属先であることを考えると、全てのクラスメソッドがClassクラスに定義されていることになります。
これでは全てのクラスから、全てのクラスメソッドが呼び出せるようなカオスな状態になってしまい、機能的によろしくないです。
ここで登場するのがSingleton Classです。
Singleton Classはそのオブジェクトに特有の隠されたクラスです。
これによってインスタンスがメソッド呼び出しの際に所属先のクラスを探索するように、クラスメソッドの場合はsingleton_classを探索するという、同じような設計を保つことができています。
singleton_classの継承関係
クラスを継承した場合、スーパークラスに定義されているインスタンスメソッドはサブクラスでも呼び出すことができますが、これはクラスメソッドも同様です。
AdminUserクラスをUserクラスが継承する場合を考えます。
class AdminUser < User
end
pry(main)> AdminUser.superclass
# => User(...)
pry(main)> User.singleton_class
# => #<Class:User(...)
pry(main)> AdminUser.singleton_class.superclass
# => #<Class:User(...)
このように、Userクラス、AdminUserクラス間だけでなく、Userクラスのsingleton_class、AdminUserクラスのsingleton_classの間にも継承関係があることがわかります。
参考にしたサイト