クラス、インスタンス、無名クラスの違いについて少しハマったのでメモ
例えばあるクラスのサブクラスを全て取得したい時は以下のコードで取得できる
class Hoge
def self.subclasses
ObjectSpace.each_object(Class).select { |klass| klass < self && klass.name != "" }
end
end
ここであるクラスが自分のクラスを継承していることを表す条件式が klass < self
なのだが、このクラスの継承に関する演算子 <
はインスタンスとクラスで呼び出すとエラーになる
class Fuga < Hoge
end
p Fuga < Hoge # true
fuga = Fuga.new
p fuga < Hoge # Error
最初の subclass method でなぜエラーにならないかというとそれは each_object(Class)
の部分でインスタンスが除外されているからだ
ObjectSpace.each_object(klass)
は指定された klass
と Object#kind_of?
の関係にある全てのオブジェクトに対して繰り返す、とある(参考)
実際にインスタンスに対して kind_of?(Class)
を呼び出してみると
p fuga.kind_of?(Class) # false
Class クラスとは関連がないと判定される
一方で無名クラスというものがあって、これは Class.new
で作成される名前のないクラスなのだが、これは Class クラスと関連があると判定される
foo = Class.new(Fuga)
p foo.kind_of?(Class) # true
無名クラスの紛らわしいところは、オブジェクトを p コマンドなどで出力するとあたかもインスタンスのようなメモリの番地が表示された出力になることだ
p fuga # #<Fuga:0x0000000126a2cc00>
p foo # #<Class:0x0000000126a3f260>
しかし実際には fuga はインスタンスで foo はクラスである
出力で見分けようと思ったら先頭の #<Class:
をみて判断するしかない
じゃあClass クラスのインスタンスはどうなるのかと思ったら、こちらは無名クラスと同じ扱いらしい
bar = Class.new
p bar # #<Class:0x000000012686b240>
p bar.kind_of?(Class) # true
確実に見分けようと思ったら kind_of?(Class)
を呼ぶしかなさそうだ
結論
インスタンスと無名クラスは紛らわしいので注意
確かめたかったら kind_of?(Class)
を呼んでみれば良い