Ruby中,方法本身並不直接以物件方式存在,然而可以透過method方法取得Method實例。例如:
>> class Some
>> def some
>> puts "#{self}'s some method"
>> end
>> end
=> nil
>> s = Some.new
=> #<Some:0x266b470>
>> mth = s.method(:some)
=> #<Method: Some#some>
>> mth.call
#<Some:0x266b470>'s some method
=> nil
>>
            
      >> def some
>> puts "#{self}'s some method"
>> end
>> end
=> nil
>> s = Some.new
=> #<Some:0x266b470>
>> mth = s.method(:some)
=> #<Method: Some#some>
>> mth.call
#<Some:0x266b470>'s some method
=> nil
>>
可以看到,取得的Method實例,self綁定的物件是s,可以取得Method實例的應用之一,就是當你的物件公開介面不一致,但又想以統一方式取得呼叫結果時。例如:
class Some
    def initialize(value)
        @value = value
    end
    def doSome(value)
        @value - value
    end
end
class Other
    def initialize(amount)
        @amount = amount
    end
    def doOther(amount)
        @amount - amount
    end
end
def utility(p, mth)
    mth.call(p)
end
s = Some.new(100)
o = Other.new(200)
puts utility(10, s.method(:doSome))   # 90
puts utility(10, o.method(:doOther))  # 190
Some的doSome方法與Other的doOther方法,都是接受一個引數並傳回運算值,雖然方法介面不同,但utility方法仍可以進行呼叫運算。
以method方法取得的Method實例,self預設有綁定物件,可以使用unbind方法解除self的綁定(會取得UnboundMethod實例),使用bind方法再度綁定self的物件。例如:
class Some
    def initialize(value)
        @value = value
    end
    def doSome(value)
        @value - value
    end
end
s1 = Some.new(10)
s2 = Some.new(20)
puts s1.doSome(5) # 5
unbind_mth = s1.method(:doSome).unbind
puts unbind_mth.bind(s2).call(5) # 15
method可以取得實例上可呼叫的方法,包括單例方法,如果你想直接取得未綁定self的實例方法,也可以使用每個類別都有的instance_method方法(這個方法無法取得單例方法)。例如:
class Some
    def initialize(value)
        @value = value
    end
    def doSome(value)
        @value - value
    end
end
s1 = Some.new(10)
s2 = Some.new(20)
unbind_mth = Some.instance_method(:doSome)
puts unbind_mth.bind(s1).call(5) # 5
puts unbind_mth.bind(s2).call(5) # 15
bind可以綁定的對象,必須是同一類別或子類別實例(但無法綁定單例方法),它甚至可以作到從子類別實例呼叫父類別中已被重新定義的方法。例如:
class Some
    def initialize(value)
        @value = value
    end
    def doSome(value)
        @value - value
    end
end
class C_Some < Some
    def doSome(value)
        @value + value
    end
end
s = Some.new(10)
c_s = C_Some.new(20)
unbind_mth = Some.instance_method(:doSome)
puts unbind_mth.bind(s).call(5)   # 5
puts unbind_mth.bind(c_s).call(5) # 15,而不是 25,因為呼叫了父類別的 doSome
puts c_s.doSome(5)                # 25
這感覺有點違反物件導向中多型的概念,一般來說,既然你已重新定義了方法,操作子類別實例的方法時應該就是被重新定義的方法,而不是父類別方法。
不過這也開啟了另一個功能性,因為Ruby中無法限制某個類別無法被繼承或無法被重新定義,為了確認執行某方法時,該方法一定是沒被重新定義過的方法,就可以使用這種功能性。例如:
class Some
    def initialize(value)
        @value = value
    end
    def doSome(value)
        @value - value
    end
end
# 一定呼叫Some的doSome
def do_some(s, v) 
    mth = Some.instance_method(:doSome).bind(s)
    mth.call(v)
end
class C_Some < Some
    def doSome(value)
        @value + value
    end
end
puts do_some(Some.new(10), 5)   # 5
puts do_some(C_Some.new(20), 5) # 15
如果願意,你也可以呼叫to_proc將一個Method轉換為lambda。例如:
>> class Some
>> def initialize(value)
>> @value = value
>> end
>> def doSome(value)
>> @value - value
>> end
>> end
=> nil
>> s = Some.new(10)
=> #<Some:0x2727618 @value=10>
>> lda = s.method(:doSome).to_proc
=> #<Proc:0x2751d50 (lambda)>
>> lda.call(5)
=> 5
>>
            
      >> def initialize(value)
>> @value = value
>> end
>> def doSome(value)
>> @value - value
>> end
>> end
=> nil
>> s = Some.new(10)
=> #<Some:0x2727618 @value=10>
>> lda = s.method(:doSome).to_proc
=> #<Proc:0x2751d50 (lambda)>
>> lda.call(5)
=> 5
>>
一個應用的例子,可以在 建構、初始與消滅 中看到:
class Some
    def initialize(value)
       @value = value
       ObjectSpace.define_finalizer(self,
                                    self.method(:finalize).to_proc)
    end
    def finalize(object_id)
        puts "Destroy #{object_id} Some(#{@value})...."
    end
end
Some.new(10)
Some.new(20)
Some.new(30)
ObjectSpace.garbage_collect   # 提示 GC
執行結果如下:
Destroy 16096056 Some(30)....
Destroy 16096140 Some(20)....
Destroy 16096224 Some(10)....
            
      Destroy 16096140 Some(20)....
Destroy 16096224 Some(10)....

