Ruby のトップレベルメソッドって結局なんなの
以下のような質問があったのでちょっと解説。
@pink_bangbi
— あつき (@atsuki09130) 2019年2月18日
僕はselfの概念がつかめておらず、バンビさんが以前書かれた記事で4つ目と5つ目(■)の項目の意味が理解できませんでした。 暇な時で構いませんので解説をいただきたいです。 pic.twitter.com/ZgX3UQ6bjj
トップレベルメソッドを理解するためには、以下の3つの Ruby の機能について理解する必要があります。
self
- トップレベル
Object
クラス
self
ってなに
まず、トップレベルメソッドを理解する前に『 self
とは何か』を知る必要があります。
self
とはメソッド内で『メソッドを呼び出したレシーバ』を参照するキーワードになります。
また、レシーバとは『メソッド呼び出しの左辺のオブジェクト』を指します。
class X def foo "foo" end def hoge # meth を呼び出したレシーバを参照する pp self # => #<X:0x00005627e8a8a050> # レシーバ、つまり x の foo メソッドを呼び出す pp self.foo # => "foo" end end x = X.new # メソッドを呼び出す際に左辺にあるオブジェクトの事を『レシーバ』という # この場合は x オブジェクトがレシーバに該当する x.hoge
また、レシーバをつけずにメソッドを呼び出した場合は暗黙的に『self
をレシーバとして』メソッドを呼び出します。
class X def foo "foo" end def hoge # レシーバがなければ self のメソッドを呼び出す # self.foo と等価 pp foo # => "foo" end end
トップレベルって?
そもそも『Ruby のトップレベルって何?』っていう話なんですが、Ruby では『class
や module
の中で定義していないメソッド』がトップレベルメソッドに該当します。
# class や module 内で定義していないメソッド def topleve_method "topleve_method" end class X # これは X のインスタンスメソッドなのでトップレベルではない def meth end end
また、トップレベルでも self
は存在し、トップレベルで self
を呼び出した場合、 main
という名前のオブジェクトを返します。
pp self # => main
main
はトップレベルで定義されている特別なオブジェクトになります。
トップレベルでメソッドを定義するとどうなるの?
トップレベルでメソッドを定義した場合、暗黙的に『Object
クラスの private
メソッド』として定義されます。
どういうことかというと
def hoge "hoge" end
というトップレベルメソッドは、
class Object private def hoge "hoge" end end
という定義と等価になります。
ちなみに Ruby における private
とは『レシーバをつけて呼び出すことが出来ないメソッド』になります。
class X # hoge を private メソッドとして定義する private def hoge end def foo # OK : レシーバをつけずに呼び出す hoge # NG : self もつけて呼び出すことは出いない self.hoge end end x = X.new # NG : レシーバをつけて呼び出すことは出来ない x.hoge
Object
クラスって何
Object
クラスは『全てのクラス』が継承しているクラスになります。
例えば String
や Array
といった組み込みクラスやユーザが定義したクラスも暗黙的に Object
クラスを継承しています。
# Module#< で任意のクラスを継承しているかチェックする # 組み込みクラスは Object を継承している p String < Object p Array < Object # ユーザが定義したクラスも暗黙的に Object を継承している class X end p X < Object
つまり 『トップレベル定義したメソッド= Object
クラスの private
メソッド』は『 Object
を継承しているクラス=全てのクラス』の『private
メソッド』として参照できます。
# Object の private メソッドとして定義される def hoge "hoge" end class X def foo # Object は親クラスなので # 親クラスの private メソッドが呼べる hoge end end x = X.new p x.foo # => "hoge" # NG : private メソッドなので直接呼べない # p "foo".hoge # OK : send だと private メソッドも明示的に呼べる p "foo".send(:hoge) # => "hoge"
結局トップレベルでメソッドって?
- トップレベルでメソッドを定義すると Object のメソッドとして定義される
- Object は全てのクラスが継承しているのでどのクラスからでも呼び出せる
- 当然トップレベルの
self
(=main
)もObject
クラスを継承しているのでトップレベルでも呼び出せる main
とトップレベルメソッドは特に関連はない
トップレベルの self
が main
っていう特別なオブジェクトなので、どうしてもそっちの方に意識が向くんですが、トップレベルの self
とトップレベルのメソッドは特に関連性はありません。
まずは、『トップレベルメソッド』というのが先にあり、『main
』っていうのが後にあります。
ちなみに『トップレベルメソッド』の他に『トップレベル定数』というものがありトップレベル定数はトップレベルメソッドの100000万倍ぐらいややこしい仕様なので知らないほうがいいです。