ruby中的可调用对象--方法

  上一篇讲了ruby中的可调用对象proc和lambda,他们都是块转换成的对象。ruby中的可调用对象还有方法。通过使用method方法,并且以方法名作为参数(字符串或者符号),就可以得到一个方法对象。

如下:

1 class C
2     def talk
3         p "hello world"
4     end
5 end
6 
7 c = C.new
8 meth = c.method :talk
9 meth.call

输出: "hello world"

  类C有个实例方法talk。类C的实例c调用方法method,并且用:talk做参数,得到一个方法对象meth。然后meth调用call,输出"hello world"。

  值得注意的是,meth这个方法对象是绑定在对象c上的方法。调用call的时候,也知道是c在调用talk方法。

  可以用Method#unbind()方法来把一个方法跟它绑定的对象相分离,该方法返回一个UnboundMethod对象,不能执行UnboundMethod对象,必须把它绑定到一个对象上,使之再次成为一个Method对象。

如下:(在上面代码的基础上)

1 class D < C
2 end
3 d = D.new
4 unbound = meth.unbind
5 new_meth = unbound.bind(d)
6 new_meth.call

输出:  "hello world"

  unbind解绑之后,绑定到C的子类D的一个实例对象d上。再对方法对象调用call。如果不想对一个已绑定的方法进行解绑,可以用如下的代码得到解绑对象:

unbound = C.instance_method (:talk)

  为什么要这样呢?存在总是合理的,绑定与解绑也有它的适用之处。

  在之前《ruby中的方法查找》中,我们知道ruby的方法调用是根据匹配到的第一个方法来调用的。那么如果要调用匹配路径上的匹配到的第二个方法呢?ruby中有super这个关键字。但是加入super关键字相当于改变了这个方法。它并不是直接调用了匹配路径上的第二个方法。而是在匹配到的第一个方法里调用第二个方法。还有,如果要调用匹配路径上的第三个匹配到的方法呢?第四个呢?这个就可以用到绑定的方法了。

如下:

 1 class C
 2     def method
 3         p "this is in C"
 4     end
 5 end
 6 
 7 class D < C
 8     def method
 9         p "this is in D"
10     end
11 end
12 
13 class E < D
14     def method 
15         p "this is in E"
16     end
17 end
18 
19 e = E.new
20 e.method
21 
22 D.instance_method(:method).bind(e).call
23 C.instance_method(:method).bind(e).call

输出:

"this is in E"

"this is in D"

"this is in C"

  如代码所示,定义类C,D,E,D继承C,E继承D。他们都定义了方法method。那么E的实例e的方法查找路径上这三个method都可以调用。如果直接调用e.method。那么肯定是调用了第一个匹配的方法,也就是类E中的方法,如输出的第一行。如果要调用类D中的方法method呢?如代码22行。把D的实例方法绑定到对象e上,然后在调用。调用类C中的方法method,同理。如23行。

  学过C++的话,看到这个可能会想到派生类指针引用基类对象。派生类是基类的特例,所以只有强制转换类型之后才能引用基类的对象。和ruby中的绑定类似,只有和父类的方法绑定以后,才能调用父类的方法。

  和上一篇一起做一个总结。ruby中可以被调用的对象有:

    块(不算是对象,可以被调用),proc(Proc类的对象),lambda(Proc类的对象),这三种都是在定义它们的作用域中执行。

    方法:绑定于对象,在所绑定的对象的作用域中执行。

    将一种对象转换成另一种对象的方法有:Proc.new(),Method#to_proc(),&操作符