どこかでメモを見逃したので、これについて説明してもらえれば幸いです。
オブジェクトの固有クラスがself.class
と異なるのはなぜですか?
class Foo
def initialize(symbol)
eigenclass = class << self
self
end
eigenclass.class_eval do
attr_accessor symbol
end
end
end
固有クラスをclass.self
と同等にする一連のロジックは、かなり単純です。
class << self
は、インスタンスメソッドではなく、クラスメソッドを宣言する方法です。これはdef Foo.bar
へのショートカットです。
したがって、クラスオブジェクトへの参照内では、self
はself.class
と同じである必要があります。これは、クラスメソッド/属性の定義のためにclass << self
がself
をFoo.class
に設定するためです。
私は混乱していますか?または、これはRubyメタプログラミングの卑劣なトリックですか?
class << self
は、クラスメソッドを宣言する単なる方法ではありません(そのように使用できます)。おそらくあなたは次のようないくつかの使用法を見てきました:
class Foo
class << self
def a
print "I could also have been defined as def Foo.a."
end
end
end
これは機能し、def Foo.a
と同等ですが、機能は少し微妙です。その秘密は、self
がそのコンテキストでオブジェクトFoo
を参照することです。そのクラスはClass
の一意の匿名サブクラスです。このサブクラスはFoo
の-eigenclassと呼ばれます。したがって、def a
は、a
の固有クラスにFoo
という新しいメソッドを作成します。これは、通常のメソッド呼び出し構文Foo.a
からアクセスできます。
次に、別の例を見てみましょう。
str = "abc"
other_str = "def"
class << str
def frob
return self + "d"
end
end
print str.frob # => "abcd"
print other_str.frob # => raises an exception, 'frob' is not defined on other_str
この例は前の例と同じですが、最初はわかりにくいかもしれません。 frob
は、String
クラスではなく、str
の固有クラスであるString
の固有クラスで定義されています。したがって、str
にはfrob
メソッドがありますが、String
のインスタンスには一般的にありません。 Stringのメソッドをオーバーライドすることもできます(特定のトリッキーなテストシナリオで非常に役立ちます)。
これで、元の例を理解できるようになりました。 Foo
の初期化メソッド内では、self
はクラスFoo
ではなく、Foo
の特定のinstanceを参照します。その固有クラスはFoo
のサブクラスですが、Foo
ではありません。それができなかったり、2番目の例で見たトリックが機能しなかったりします。だからあなたの例を続けるには:
f1 = Foo.new(:weasels)
f2 = Foo.new(:monkeys)
f1.weasels = 4 # Fine
f2.monkeys = 5 # Also ok
print(f1.monkeys) # Doesn't work, f1 doesn't have a 'monkeys' method.
お役に立てれば。
最も簡単な答え:固有クラスはインスタンス化できません。
class F
def eigen
class << self
self
end
end
end
F.new.eigen.new #=> TypeError: can't create instance of virtual class
イェフダ・カッツは、「 Rubyでのメタプログラミング:自己についてのすべて 」で微妙な点について説明しています。