はじめに
特異クラス、特異メソッドについて、ふと調べてみようと思ったので、その内容をまとめてみます。
特異メソッド
オブジェクト固有のメソッドのことです。
定義するメソッド名の前に、オブジェクト名を書くことで定義できます。
なお、定義する際に、そのオブジェクトが既に存在していないといけません。
class Klass
end
apple = Klass.new
orenge = Klass.new
def apple.tokui_method
puts "I'm apple"
end
apple.tokui_method
# => I'm apple
orenge.tokui_method
# => undefined method `tokui_method'
もしも定義する際にその指定するオブジェクトが存在していなかった場合、エラーとなります。
class Klass
end
def apple.tokui_method
puts "I'm apple"
end
apple = Klass.new
apple.tokui_method
# => undefined local variable or method `apple' for main:Object (NameError)
特異メソッド内でsuperを呼ぶと、クラスに定義されている同名のメソッドが呼ばれます。
また、そのメソッドのオーバーライドが出来るという便利な点もあります。
class Klass
def my_name
puts "I'm fruits"
end
end
apple = Klass.new
orenge = Klass.new
def apple.my_name
super
puts "I'm apple"
end
apple.my_name
# => I'm fruits
# => I'm apple
orenge.my_name
# => I'm fruits
特異クラス
あるオブジェクトに特異メソッドを定義した際に、そのメソッドが定義されるクラスが特異クラスです。
通常のクラスとは違い、特異クラスはあるオブジェクトだけが使用するクラスです。
そのため、特異クラスからオブジェクトを作成したり、サブクラスを作成することは禁止されています。
オブジェクトの特異クラスを確認するには
Object#singleton_classを使用します。
class Klass
end
obj = Klass.new
# => #<Klass:0x00007f8fb584cc08>
TokuiClass = obj.singleton_class
# => #<Class:#<Klass:0x00007f8fb584cc08>>
TokuiClass.new
# => can't create instance of singleton class (TypeError)
class SubTokuiClass < TokuiClass
end
# => can't make subclass of singleton class (TypeError)
また、前述した特異メソッドは特異クラスに定義されています。
特異メソッドに関しては
singleton_class.method_defined?で確認することができます。
class Klass
end
obj = Klass.new
def obj.tokui_method
:tokui_method
end
p obj.class.method_defined? :tokui_method
# => false
p obj.singleton_class.method_defined? :tokui_method
# => true
特異クラスが作成されるタイミング
オブジェクトが生成されたタイミングでは、そのオブジェクトの特異クラスは存在していません。
特異クラスは以下の場合に作成されます。
1, 特異メソッドを定義するタイミング
2, 特異クラス定義式を評価したタイミング
3, Object#singleton_classでオブジェクトに対して特異クラスの確認をするタイミング
特異メソッドを定義するタイミング
obj = Klass.new
def obj.tokui_method
:tokui_method
end
特異クラス定義式を評価したタイミング
class << obj
end
Object#singleton_classでオブジェクトに対して特異クラスの確認をするタイミング
確認しようとしたタイミングで、特異クラスが存在していなければ新たに作成されます。
obj.singleton_class
オブジェクトと特異クラス
特異クラスは、オブジェクトのクラスのサブクラスになります。
class Klass
end
obj = Klass.new
obj.singleton_class.supperclass
# => Klass
特異クラスがあるオブジェクトに対して作成されると、そのオブジェクトは特異クラスのインスタンスになりますが、Rubyの中では
特異クラスのインスタンスであるということは無視されるようになっており、常にオブジェクトのクラスのインスタンスとして扱われます。
Object#instance_of?で引数の直接のインスタンスか確認できるため、これを使用してみます。
obj.instance_of? obj.singleton_class
# => false
obj.instance_of? Klass
# => true
特異クラスをもてないオブジェクト
Rubyで以下のオブジェクトは特異クラスを持つことが出来ません。
- 数値
- Symbol
- true, false, nil
整数とsymbleはエラーが返ってきます
1.singleton_class
# => can't define singleton (TypeError)
:symble.singleton_class
# => can't define singleton (TypeError)
true, false, nil は自身のクラスが特異クラスとして返ってきます
p true.singleton_class
# => TrueClass
p false.singleton_class
# => FalseClass
p nil.singleton_class
# => NilClass