【Ruby v2.6】インスタンスメソッド その3(特異メソッド)
前置き
Ruby のインスタンスメソッドについてまとめます。 その3は、特異メソッドについて。
特異メソッドとは
インスタンスは、クラスで定義された内容で生成されますが、「特異メソッド」は生成されたインスタンス自身に、新たに定義するインスタンスメソッドです。 インスタンスオブジェクトに直接定義するので、使用できるのは定義したインスタンスオブジェクトのみで、同じクラスでも違うインスタンスオブジェクトでは使用できません。
書き方
まずインスタンスオブジェクトを生成し、def [インスタンス].[メソッド名]
という書き方で定義します。
サンプル
以下、サンプルのクラスで例を示します。
class A # 通常のインスタンスメソッド def a_public_method p "called a_method." end end
まず、通常のクラス A の定義です。ここからインスタンスを生成して、特異メソッドを定義してみます。
obj1 = A.new # => クラス A のインスタンス obj2 = A.new # => 同じクラスの別のインスタンス def obj1.eigen_method # => obj1 に特異メソッドを定義する。 p "called obj1.eigen_method" end
この状態で、obj1
、obj2
について eigen_method
メソッドを呼び出してみると、obj1
だけが呼び出せることが分かります。
obj1.eigen_method # => "called eigen_method." obj2.eigen_method # => NoMethodError 例外
特異メソッドの格納先「特異クラス」
通常のインスタンスメソッドは、インスタンスを定義したクラスに格納されていますが、特異メソッドの定義(格納先)は、インスタンスオブジェクト自身に格納されるわけではなく、「特異クラス」というクラスに格納されています。(「シングルトンクラス」とも呼ばれます)
インスタンスオブジェクトの特異クラスは、.singleton_class
で参照できます。
obj1 # => #<A:0x00000*******> A のインスタンスオブジェクトを表す obj1.singleton_class # => #<Class:#<A:0x00000*******>> 「#<Class:」 は特異クラスを表す obj1.singleton_class.superclass # => A obj1 の特異クラスは、A のサブクラスになっています。
クラス A で定義されているインスタンスメソッド、obj1 の特異クラスで定義されているインスタンスメソッドを表示すると、以下のようになります。
A.instance_methods(false) # => [:a_method] obj1.singleton_class.instance_methods # => [:eigen_method] obj2.singleton_class.instance_methods # => []
obj1
で同じように使用できるインスタンスメソッドですが、特異メソッドはクラス A ではなく、特異クラスに格納されています。また、obj2
では特異メソッドを定義していないので、特異メソッドは空です。
「特異クラス」と「特異メソッド」の関係
図で表現すると、下記のようになります。
インスタンスobj1
に特異メソッドを追加すると、図のようにクラス A のサブクラスとして、obj1
の特異クラスが生成され、特異メソッドの定義はそこに格納されます。¥
どんな場面で使用するか?*1
複数のインスタンスを生成していて、ある固有のインスタンスのみに必要な機能がどうしても出てきてしまった場合で、かつ、クラス定義を修正すると影響範囲が大きくなりすぎてしまうような場合に、応急処置的に定義して使用する使い方がほとんどかと思います。
基本的には、新規開発の設計段階などでは、しっかりとクラス定義を書くべきで、わざわざ「特異メソッドの利用を前提」として設計する必要は無いはずです。