module B
def a
print 'B'
super
end
end
class A
extend B
def a
print "A"
end
def self.a
print "A"
end
end
a = A.new
a.extend B
puts a.a # => BA
puts A.a # => A
Kernel#extendメソッドがクラスオブジェクトとクラスインスタンスオブジェクトで異なる動作をするのはなぜですか?インスタンスを拡張すると、継承のチェーンの前にモジュールが追加されるように見えますが、代わりにクラスを拡張すると、モジュールがクラスの上に配置されます。
まず、いくつかの概念を紹介します。
まず、を使用してクラスメソッドをdef self.a
定義することは、クラスのシングルトンクラスでメソッドを定義することと同じです。
class C
def self.a; end
class << self
def b; end
end
end
C.method(:a) # => #<Method: C.a>
C.method(:b) # => #<Method: C.b>
さらに、オブジェクトのメソッドは、そのオブジェクトのシングルトンクラスのインスタンスメソッドです。
C.singleton_class.instance_method(:a) # => #<UnboundMethod: #<Class:C>#a>
C.singleton_class.instance_method(:b) # => #<UnboundMethod: #<Class:C>#b>
#b
上記で定義した方法を見るとself
、プレフィックスが付いていないことがわかります。したがって、これは単なるインスタンスメソッドです。
次に、シングルトンクラス#extend
と同じ#include
です。
module M; end
class C1
extend M
end
class C2
class << self
include M
end
end
C1.ancestors # => [#<Class:C2>, M, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
C2.ancestors # => [#<Class:C1>, M, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
通知はどのようにM
今の祖先の一部であるC1
とC2
同じようインチ
含める(または拡張する)M
ことも、次のように達成できます。
C1.extend M
C2.singleton_class.include M
最後に、#include
モジュールを作成すると祖先がどうなるかに注意してください。
module M1; end
module M2; end
class C; end
C.include M1
C.ancestors # => [C, M1, Object, Kernel, BasicObject]
C.include M2
C.ancestors # => [C, M2, M1, Object, Kernel, BasicObject]
すべては、#include
(受信した後に挿入されたモジュールが得られC
先祖連鎖この場合)。
それでは、あなたの定義を見てみましょう(本文を省略します):
module B; end
class A
extend B
end
覚えておいてください、#extend
と同じである#include
に#singleton_class
。したがって、次のように書き直すことができます。
module B; end
class A; end
A.singleton_class.include B
シングルトンクラスの祖先は、B
最初の項目の後にあります。これは、A
クラスメソッドが定義されているシングルトンクラスです(したがって、クラスメソッドは、問題のクラスのシングルトンクラスのインスタンスメソッドにすぎないことに注意してください)。
A.singleton_class.ancestors # => [#<Class:A>, B, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
コードの2番目の部分に移ります。
a = A.new
a.extend B
を使用して書き直します#include
:
a = A.new
a.singleton_class.include B
祖先を確認しましょう:
a.singleton_class.ancestors # => [#<Class:#<A:0x00007f83e714be88>>, B, A, Object, Kernel, BasicObject]
この場合も#include
、モジュールを祖先チェーンの最初の要素の後に配置し、結果として。のB
前に配置しましたA
。
これは、(つまり)に送信#a
するときに、に応答する最初の祖先を探すことを意味します。この場合はそうです。次に、を呼び出します。これは、に応答する祖先チェーンに沿って続行されます。a
a.a
#a
B
B
super
A
#a
さてA.a
、それは異なります。A
のシングルトンクラスの祖先を思い出してください。
A.singleton_class.ancestors # => [#<Class:A>, B, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
のB
後に来ることに注意してください#<Class:A>
。のクラスメソッドである、に#<Class:A>
すでに応答しています。そのメソッドはを呼び出さないため、は呼び出されません。したがって、同じ出力は得られません。#a
A
super
B#a
あなたが持っているしたい場合はB
前に#<Class:A>
、あなたはプリペンドBにする必要があると思いますA
のシングルトンクラス。最初のアイテムの後に#prepend
オブジェクトを挿入する、とは異なり#include
、祖先チェーンの最初にオブジェクトを挿入します(これを機能させるにextend B
は、コード内のを削除する必要があります。そうしないB
と、すでに祖先である場合は何も起こりません)。
A.singleton_class.prepend B
A.singleton_class.ancestors # => [B, #<Class:A>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
呼び出すA.a
と、と同じa.a
、つまりprintが生成されますBA
。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加