Object/Class/Module/Kernelにメソッドを追加すると, これらや一般のクラス, モジュールやインスタンスに対してどのような影響があるか, 調べるコードを書いてみた. 追加するメソッドはインスタンスメソッドと特異メソッドで, 両方publicの可視属性です.
コード
#
class Object
def meth_test_oi; "#{self} - #{__method__}"; end
def self.meth_test_ois; "#{self} - #{__method__}"; end
end
class Class
def meth_test_ci; "#{self} - #{__method__}"; end
def self.meth_test_cis; "#{self} - #{__method__}"; end
end
class Module
def meth_test_mi; "#{self} - #{__method__}"; end
def self.meth_test_mis; "#{self} - #{__method__}"; end
end
module Kernel
def meth_test_ki; "#{self} - #{__method__}"; end
def self.meth_test_kis; "#{self} - #{__method__}"; end
end
class FooClass; end
module BarModule; end
puts "## Object/Class/Module/Kernelにメソッドを追加したとき,"
puts "## 適当なクラス/モジュール/インスタンスにおいて, メソッドが追加されているか否か"
target_methods = [
:meth_test_oi,
:meth_test_ci,
:meth_test_mi,
:meth_test_ki,
:meth_test_ois,
:meth_test_cis,
:meth_test_mis,
:meth_test_kis,
]
target_classes = [ Object, Class, Module, ]
target_modules = [ Kernel, ]
target_instances = [
[1,2,3],
Array,
FooClass,
BarModule,
]
targets = target_classes +
target_modules +
target_instances
target_methods.each do |mm|
puts "* #{mm} の結果"
targets.each do |t|
begin
p t.send mm
rescue =>e
msg = e.message.gsub(/\n+Did you mean.+$/m, '')
puts "#{msg} (#{e.class})"
end
end
puts
end
#
結果
## Object/Class/Module/Kernelにメソッドを追加したとき,
## 適当なクラス/モジュール/インスタンスにおいて, メソッドが追加されているか否か
* meth_test_oi の結果
"Object - meth_test_oi"
"Class - meth_test_oi"
"Module - meth_test_oi"
"Kernel - meth_test_oi"
"[1, 2, 3] - meth_test_oi"
"Array - meth_test_oi"
"FooClass - meth_test_oi"
"BarModule - meth_test_oi"
* meth_test_ci の結果
"Object - meth_test_ci"
"Class - meth_test_ci"
"Module - meth_test_ci"
undefined method `meth_test_ci' for Kernel:Module (NoMethodError)
undefined method `meth_test_ci' for [1, 2, 3]:Array (NoMethodError)
"Array - meth_test_ci"
"FooClass - meth_test_ci"
undefined method `meth_test_ci' for BarModule:Module (NoMethodError)
* meth_test_mi の結果
"Object - meth_test_mi"
"Class - meth_test_mi"
"Module - meth_test_mi"
"Kernel - meth_test_mi"
undefined method `meth_test_mi' for [1, 2, 3]:Array (NoMethodError)
"Array - meth_test_mi"
"FooClass - meth_test_mi"
"BarModule - meth_test_mi"
* meth_test_ki の結果
"Object - meth_test_ki"
"Class - meth_test_ki"
"Module - meth_test_ki"
"Kernel - meth_test_ki"
"[1, 2, 3] - meth_test_ki"
"Array - meth_test_ki"
"FooClass - meth_test_ki"
"BarModule - meth_test_ki"
* meth_test_ois の結果
"Object - meth_test_ois"
"Class - meth_test_ois"
"Module - meth_test_ois"
undefined method `meth_test_ois' for Kernel:Module (NoMethodError)
undefined method `meth_test_ois' for [1, 2, 3]:Array (NoMethodError)
"Array - meth_test_ois"
"FooClass - meth_test_ois"
undefined method `meth_test_ois' for BarModule:Module (NoMethodError)
* meth_test_cis の結果
undefined method `meth_test_cis' for Object:Class (NoMethodError)
"Class - meth_test_cis"
undefined method `meth_test_cis' for Module:Class (NoMethodError)
undefined method `meth_test_cis' for Kernel:Module (NoMethodError)
undefined method `meth_test_cis' for [1, 2, 3]:Array (NoMethodError)
undefined method `meth_test_cis' for Array:Class (NoMethodError)
undefined method `meth_test_cis' for FooClass:Class (NoMethodError)
undefined method `meth_test_cis' for BarModule:Module (NoMethodError)
* meth_test_mis の結果
undefined method `meth_test_mis' for Object:Class (NoMethodError)
"Class - meth_test_mis"
"Module - meth_test_mis"
undefined method `meth_test_mis' for Kernel:Module (NoMethodError)
undefined method `meth_test_mis' for [1, 2, 3]:Array (NoMethodError)
undefined method `meth_test_mis' for Array:Class (NoMethodError)
undefined method `meth_test_mis' for FooClass:Class (NoMethodError)
undefined method `meth_test_mis' for BarModule:Module (NoMethodError)
* meth_test_kis の結果
undefined method `meth_test_kis' for Object:Class (NoMethodError)
undefined method `meth_test_kis' for Class:Class (NoMethodError)
undefined method `meth_test_kis' for Module:Class (NoMethodError)
"Kernel - meth_test_kis"
undefined method `meth_test_kis' for [1, 2, 3]:Array (NoMethodError)
undefined method `meth_test_kis' for Array:Class (NoMethodError)
undefined method `meth_test_kis' for FooClass:Class (NoMethodError)
undefined method `meth_test_kis' for BarModule:Module (NoMethodError)
インスタンス関係や継承関係を理解するのにいいかも.
解説
特異メソッド(クラスメソッド)の結果については割とわかりやすいと思われるため, インスタンスメソッドを追加した影響について, Classクラスの結果を例に見ておこう:
* meth_test_ci の結果
"Object - meth_test_ci"
"Class - meth_test_ci"
"Module - meth_test_ci"
undefined method `meth_test_ci' for Kernel:Module (NoMethodError)
undefined method `meth_test_ci' for [1, 2, 3]:Array (NoMethodError)
"Array - meth_test_ci"
"FooClass - meth_test_ci"
undefined method `meth_test_ci' for BarModule:Module (NoMethodError)
Classクラスに追加したインスタンスメソッド
Class#meth_test_ci
は, Object, Class, Moduleのクラスメソッドにもなっている. またArray, FooClassからもクラスメソッドとして呼び出せる. これはなぜだろうか?
Object, Classなど上記5つは, クラスである(newできる). と同時に, Classクラスのインスタンスでもある: たとえば
Object.class =>Class
, Object.instance_of? Class =>true
である (ややこしいがModuleについてもModule.class =>Class
だ).
したがって,
Class#meth_test_ci
は, Classのインスタンス経由で呼び出せる. これはclass BarClass
def meth; 1; end
end
bar_inst = Bar.new
bar_inst.meth # =>1
と同じことだ.
では,
Class#meth_test_ci
(インスタンスメソッド) を, なぜ Class.meth_test_ci
(クラスメソッド) として呼び出せるのか?
実は
Class.class =>Class
, Class.instance_of? Class =>true
である. つまり, Classクラスをインスタンスと見た場合, そのクラスはClass—-すなわち自分自身—-である.bar_inst.meth
を呼び出すのと同様の例を作ってみよう.class Class
def meth; 2; end
end
inst = Class
class_inst = Class.new
inst.meth # =>2
class_inst.meth # =>2
inst.class # =>Class
class_inst.class # =>Class
おわかりいただけたであろうか……?
Paolo Perrotta (角 征典 訳) の『メタプログラミングRuby』のオブジェクトモデルの章にあるオブジェクトモデルの図を読むと, 理解しやすいと思う. 初版は2010年に技術評論社から, 2015年には第2版がオライリーから出ている.
0 件のコメント:
コメントを投稿
何かありましたら、どうぞ: