Ruby的method_missing、respond_to?以及respond_to_missing?
Ruby的method_missing、respond_to?以及respond_to_missing?
关于method_missing
默认情况下,当调用obj.f()
时,将从它的单例类、继承链中查找方法f,如果最终没有找到f方法,则抛出错误:undefined method f。
1 | class C |
例如,将method_missing
作为对象的实例方法定义在类中:
1 | class C |
重载method_missing
时,它的第一个参数是查找失败的方法名称(上例中为:f
),另外两个参数分别用于接收调用方法时给定的参数和语句块。
一般来说,会在重载的method_missing
中使用super继续上抛方法缺失错误,但非必须。
因为可以自定义method_missing
,使得很多方法看上去是存在的,只不过这些方法都被method_missing
处理了。比如,只要是以_name
结尾的方法,都认为是存在的方法:
1 | class C |
关于respond_to?和respond_to_missing?
method_missing
用于定义方法查找失败时的处理方式,而respond_to?
则判断方法是否能查找成功,即接收者对象能否响应所调用的方法。默认只查找public方法,可指定相关参数使其也查找private和protected方法。
例如:
1 | class C |
从上面最后一行代码的结果中可知,respond_to?
无法响应那些能匹配method_missing
的方法名,但是这些方法既然定义在method_missing
中,可能就是想表达该方法已经存在。
为了能respond那些能匹配method_missing
的方法名,重载该方法即可:
1 | class C |
虽然现在所有以_name
为后缀的方法已经『存在』了,可以让respond_to?
返回true,也可以使用obj.xyz_name
的方式来调用该类方法。但是还不够,在不是使用obj.xyz_name
的方式调用时,它仍然是不存在的,它毕竟不是真实存在的。比如method方法、public_method方法等将其进行转换时会报错,提示找不到方法,也即无法转换:
1 | class C |
Ruby为了让不存在但却能匹配method_missing
的方法更像已存在的方法,还加入了一个respond_to_missing?
替代respond_to?
,重载前者而非后者,使得在真正操作那些缺失但能匹配method_missing
的方法时认为其是存在的。
1 | class C |
使用建议
大家都建议,在重载方法method_missing
时,也总是重载respond_to_missing?
,这是合理的建议。