Rubyメタクラスの紹介



翻訳者のメモ:この投稿は論理的な展開であり、むしろ投稿の「バックストーリー」です。 私たちはユーザーmurrによってコメントとインクルードを掘り下げ 、コメントを求められました。



Rubyのクラスとオブジェクトは複雑に接続されており、何が何であるかをすぐに理解することはできません。 クラスの特別な目的のために、クラスがRubyオブジェクトでもあるという事実を見失うことは非常に簡単です。 これらを「通常の」オブジェクトと区別する2つの点があります。これらは、新しいオブジェクトを作成するためのモデルとして機能し、クラス階層の一部です。 そうです、クラスはそれ自身(オブジェクト)、スーパークラス(親)、サブクラス(子)のインスタンスを持つことができます。



クラスがオブジェクトの場合、独自のクラスが必要です。 Rubyのすべてのクラス(オブジェクトとして)のクラスClassクラスです:



#       Dog = Class.new #     class Dog # -    end Dog.class => Class
      
      







メソッド検索パス



メソッド検索パスのコンテキストでは、クラスとオブジェクトの関係が重要になります。 オブジェクトのメソッドを呼び出すと、Rubyは最初にオブジェクトのクラスでそのメソッドを探します。 ここでメソッドが見つからない場合、次はオブジェクトのクラスのスーパークラスになり、クラスの階層全体で最初のクラス(Ruby 1.9ではBasicObject)まで続きます。 以下の図は、適切なメソッドを求めてRubyがクラス階層をどのように移動するかを示しています。







クラスのメソッド検索パスも機能します。 クラスメソッドを呼び出すと、最初にClassクラスであることが判明したオブジェクトのクラスがスキャンされ、次にBasicObjectまでスーパークラスモジュールなどで検索が続行されます。



 Dog.class => Class Class.superclass => Module Module.superclass => Object Object.superclass => BasicObject
      
      





そして視覚的に同じこと:







ご覧のとおり、 BasicObjectObjectはクラス階層のルートです。つまり、すべてのオブジェクトは、通常であるかクラス(オブジェクトなど)であるかに関係なく、これらのクラスで定義されたクラスインスタンスメソッドを継承します。



シングルトン法



クラスの主な機能は、インスタンスの動作を決定することです。 たとえば、犬の一般的な動作はDogクラスにあります。 ただし、Rubyでは、個々のオブジェクトに独自の動作を追加できます。 つまり このクラスの他のインスタンスでは利用できないDogクラスの別のオブジェクトにメソッドを追加できます。 ほとんどの場合、このようなメソッドは単一のオブジェクトにのみ属するため、シングルトンメソッドと呼ばれます。



この場合も、シングルトンメソッドはオブジェクトに対して直接定義され、このオブジェクトのクラス内では定義されません。



 snoopy = Dog.new def snoopy.alter_ego "Red Baron" end snoopy.alter_ego # => "Red Baron" fido.alter_ego # => NoMethodError: undefined method `alter_ego' for #<Dog:0x0000000190cee0>
      
      







メタクラス



上記のように、オブジェクトのメソッドを呼び出すと、Rubyはオブジェクトのクラスでそのメソッドを検索します。 シングルトンメソッドの場合、このクラスの他のインスタンスでは使用できないため、オブジェクトのクラスの外側にあることは明らかです。 彼らはどこにいるの? そして、通常のメソッド検索パスに違反することなく、Rubyは通常のクラスインスタンスメソッドとともにシングルトンメソッドをどのように見つけることができますか?



Rubyがこれを彼の特徴的なエレガントなスタイルで実装していることがわかります。 最初に、オブジェクトのシングルトンメソッドをホストする匿名クラスが作成されます。 次に、この匿名クラスがオブジェクトのクラスの役割を果たし、オブジェクトのクラスが匿名クラスのスーパークラスになります。 したがって、通常のメソッド検索パターンは変更されないままです。シングルトンメソッド(匿名クラス内)とクラスインスタンスメソッド(匿名クラスのスーパークラス内)の両方が、通常のメソッド検索パスに従って検出されます(下図を参照)。



この動的に作成された匿名クラスには、メタクラス、ハロクラス(ゴースト)、シングルトンクラス、および固有クラス(固有クラス)という多くの名前があります。 「eigen」という言葉はドイツ語から来ており、おおよそ「自分のもの」と訳されています。



翻訳者のメモ:投稿のテキスト(元のタイトルのように)の下で、著者は「eigenclass」という用語を使用しますが、翻訳では「metaclass」という用語を選択しました。 私の意見では、彼は少なくともロシアの耳を歪ませます。 この意味は変わりません。






注:オブジェクト名(つまりdef snoopy.alter_ego )を使用してシングルトンメソッドを定義することに加えて、特別な構文を使用してオブジェクトのメタクラスにアクセスすることもできます。



 class << snoopy def alter_ego "Red Baron" end end
      
      





class << ”コンストラクトは、指定したオブジェクトのメタクラスを開き、このメタクラスのスコープ内で直接作業する機会を提供します。



クラスメソッド



クラスはオブジェクトであるため、シングルトンメソッドを持つこともできます。 実際、クラスのメソッドとして考えていたのは、実際にはシングルトンメソッドにすぎません。1つのオブジェクトに属するメソッドは、実際にはクラスです。 他のシングルトンメソッドと同様に、クラスメソッドはメタクラス内にあります。



クラスメソッドはさまざまな方法で定義できます。



 class Dog def self.closest_relative "wolf" end end class Dog class << self def closest_relative "wolf" end end end def Dog.closest_relative "wolf" end class << Dog def closest_relative "wolf" end end
      
      





上記の例はすべて同一です。これらはすべてDogオブジェクトのメタクラスを開き、その中にメソッド(クラス)を定義します。



メタクラスの公開



メタクラスは匿名であるだけでなく、通常の状況では私たちから隠されています。 そして、これはメソッドを見つける最初の目的地になるように設計されているという事実にもかかわらずです:



 class << Dog def closest_relative "wolf" end end Dog.class # => Class
      
      





上記のコードでは、 DogクラスはそのクラスがClassクラスであることを引き続き示しますが、メタクラスを作成し、 Dogオブジェクトのクラスをそのメタクラスで置き換えるクラスメソッドを追加しました。 ただし、オブジェクトのメタクラスを明示するのに役立つ方法があります。



 class Object def metaclass class << self self end end end
      
      





このコードは最初はわかりにくいかもしれませんので、詳しく見ていきましょう。



まず、 ObjectクラスがRubyのすべてのクラスの祖先であることを知っているため、どこでも利用できるようにするメソッドを定義するのに適した場所です。



上記のコードを理解するには、 自己の価値がどこでどのように変化するかを追跡する必要があります。 メタクラス()メソッド内で直接、 selfはこのメソッドを呼び出したオブジェクトです。 次に、構文「 class << 」を使用してこのオブジェクトのメタクラスを開きます。 メタクラスのスコープ内にいるので、 自己の値は変わり、オブジェクトのメタクラスを参照します。 メタクラスからselfを返し、Rubyに組み込まれたto_sオブジェクト識別子を呼び出します。これにより、とらえどころのないメタクラスを垣間見ることができます。



次に、 metaclass()メソッドを使用してメタクラスをフィッシングアウトする方法を示します。



 class Dog end snoopy = Dog.new => #<Dog:0x00000001c4a170> snoopy.metaclass => #<Class:#<Dog:0x00000001c4a170>> snoopy.metaclass.superclass => Dog
      
      







Rubyのto_sメソッド



to_sメソッド Objectクラスで定義され、 呼び出されたオブジェクトの文字列表現を返します。 文字列は、オブジェクトのクラス名とオブジェクトの一意の識別子の組み合わせです。 たとえば、 #<Mouse:0x00000001c4a170>



ClassObject、 Stringなどのクラス( Classクラスのインスタンス)は、このメソッドをオーバーライドして、単に名前を返すようにします。



 Dog.to_s => "Dog"
      
      





実際、 to_sメソッドClassクラスのスーパークラスであるModuleクラスでオーバーライドされます。 これは、 ModuleクラスのRubyドキュメントでto_sメソッドを説明する方法です。



モジュールまたはクラスを表す文字列を返します。 基本クラスとモジュールの場合、これはそれらの名前です。 シングルトーンの場合、それらが接続されているオブジェクトに関する情報も表示します。


シングルトンに関する発言に注意してください。 これは、 スヌーピーメタクラスのto_sを呼び出すと、名前のない「クラス」に続いて、このクラスが関連付けられているオブジェクトの識別子を取得する理由を説明します: #<Class:#<Dog:0x00000001c4a170 >>Dogの元の(メタクラスではなく)クラスを参照するメタクラスのスーパークラスに対してto_sを呼び出すことにより 、単純に名前「Dog」を取得します。



メタクラスとクラスの継承



オブジェクトがクラスのスーパークラスで定義されたクラスインスタンスメソッドにアクセスできるように、クラスは祖先で定義されたクラスメソッドにアクセスできます。



 class Mammal def self.warm_blooded? true end end class Dog < Mammal def self.closest_relative "wolf" end end Dog.closest_relative # => "wolf" Dog.warm_blooded? # => true
      
      





別の課題。 クラスは先祖のクラスメソッドをどのように継承しますか? 結局のところ、クラスのスーパークラスはクラスインスタンスのメソッドを見つける方法にありますが、クラス自体のメソッドを見つける方法にはありません。



Dogクラスのインスタンスのメソッド検索パス:

 fido -> Dog -> Mammal -> Object...
      
      





Dogオブジェクトのメソッド検索パス:

 Dog -> Class -> Module -> Object...
      
      





ここでも、メソッド検索テンプレートの不可侵性を保証し、同時にスーパークラスクラスのメソッドへのアクセスを提供するために、興味深い操作が使用されます。



まず、 Dogクラスのクラスメソッド(シングルトンメソッド)を定義すると、メタクラスが作成されます。 このメタクラスは、 ClassクラスではなくDogオブジェクトのクラスになります。 Classクラスは、メソッド検索チェーンの上位に「プッシュ」され、メタクラスのスーパークラスになります。 ここで、 Dogクラスのスーパークラス(たとえば、 Mammal )のクラスメソッドを定義すると、作成されたメタクラスはDogオブジェクトのメタクラスのスーパークラスになり、 Classクラスをさらに「プッシュ」します。 この説明は油のように透明であるため、上記を明らかにするチャートがあります。







ここで入手してください。 Rubyでオブジェクトのメソッドを見つけるまでの道のり。 まあ、ほとんど。 ここでは、モジュールの不純物がどこに適合するかを考慮しませんでした。 このことを別の投稿でお見せする時間があることを願っています。






翻訳者のメモ:これはまさに不純物がここに収まる場所であり、私の翻訳で説明されています。includeとextendを掘り下げているので、完全を期すために読むことをお勧めします。



All Articles