ruby中的方法查找
ruby中的方法调用都是 对象.方法 的形式,那么对象如何找到这个方法呢?
首先必须了解祖先链的概念,祖先链就是从一个类开始,到它的父类,再到父类的父类...一直到最终的起点(ruby中是BasicObject类)。这期间经历过的路径就是祖先链。
1混含模块和继承的方法查找
对于一个实例对象,先找它属于的类中是否有对应的实例方法,然后看这个类中是否有模块,如果有,查找模块中是否有对应的方法,如果没有,则查找父类。先看父类的实例方法,再看父类中是否有模块,再看父类的父类..一直到最后,BasicObject类和Kernel模块。
如果还没有,则会去查看method_missing函数,这个函数是内建函数。这个函数默认是报错,当然你也可以重写这个方法,来对没有找到的方法在其中进行处理。
如下:
module M
def method
puts "this is method in module M"
end
end class C
include M
end class D < C;end D.new.method
p D.ancestors
输出是
this is method in module M
[D, C, M, Object, Kernel, BasicObject]
如这个例子,类D的对象D.new要找method方法:先找D中的实例方法,没有,D中也没有模块。D的父类是C,先找C的实例方法,没有,但C中有模块M,模块M中有method方法,这样就找到了。这个顺序就是按照祖先链的顺序。
这种查找能到什么程度呢?如祖先链表示的,接下来就是Object类,这是ruby中一切对象的开始。其中有一个模块Kernel。也就是说,如果你在Kernel中定义了一个方法,那么ruby中的所有对象都可以用这个方法。
2含有多个相同的方法时的方法查找
含有多个相同的方法时,会匹配第一个找到的方法。
如下:
module M
def method
puts "this is method in module M"
end
end module N
def method
puts "this is method in module N"
end
end class C
include M
include N
end C.new.method
p C.ancestors
输出结果
this is method in module N
[C, N, M, Object, Kernel, BasicObject]
类C中包含两个模块M和N,M和N都有method方法。那么调用哪个呢?如果在同一个类中,ruby中新定义的方法会覆盖旧的方法。类似的,模块N相对M是后混如类C的,所以会调用N中的方法。另一个方面,从祖先链来看,N也是排在M的前面,因此也是先调用N的方法。
祖先链中是模块是怎么排序的呢?祖先链中,一个模块M恰好在包含它的类C中的上一个位置。如这个例子,类C先包含了M,祖先链中是C,M。然后又包含了N,N又恰好在C的上一个位置,于是就变成了C,N,M。
3包含单例类的方法查找
前面的两种情况是不含单例类的情况,如果含有单例类,就要先考虑单例类了。
单例类:简单的说就是某个对象特有的类。它只能属于一个对象(即使是同一个类的其他对象实例也不行),因此称为单例类。
ruby中的每个对象实际上都有两个类:多个对象实例共享的类和单例类。对象调用的方法,就是这两个类中的实例方法,以及祖先类和混含模块中的方法。
有单例类的时候,对象的方法查找先查找单例类,然后是单例类混含的模块,然后是对象所属的类,以此类推。
单例类的父类是对象所属的类。
如下:
module M
def method
puts "this is method in module M"
end
end class C
end c = C.new
class << c
def method
puts "this is method in c' singleton class"
end
include M
p ancestors
end c.method
输出是
[M, C, Object, Kernel, BasicObject]
this is method in c' singleton class
单例类,并没有在祖先链中表示出来,但是调用的方法确实是单例类的方法。然后是混含的模块M,然后是父类,以此类推。从祖先链可以看出,单例类的父类是C,是对象c所属的类。
HELP
在这里出现了一个问题,假如类C中也包含类模块M,那祖先链理论上说应该是M,C,M,Object,Kernel,BasicObject
如下:
module M
end
class C
include M
end
c = C.new
class << c
include M
p ancestors
end
p C.ancestors
输出结果是:
[C, M, Object, Kernel, BasicObject]
[C, M, Object, Kernel, BasicObject]
单例类里混含的模块没有出现在祖先链里,c的单例类和类C的祖先链一样了。
假设类C中包含的不是模块M,而是另一个模块N。
如下:
module M
end
module N
end
class C
include N
end
c = C.new
class << c
include M
p ancestors
end
p C.ancestors
输出结果
[M, C, N, Object, Kernel, BasicObject]
[C, N, Object, Kernel, BasicObject]
此时,结果和我预期的一样。祖先链中仍然是有c的单例类混含的模块M的。
这是为什么呢?难道是说,如果单例类里和祖先链上的其他类混含了同样的模块,单例类中的模块名字不显示了?
另外我也在类Object中包含了M,结果是[C, N, Object, M, Kernel, BasicObject],c的单例类中的模块M也没有。如果是包含N,结果是[M, C, N, Object, N, Kernel, BasicObject],又和预期的一样。难道是单例类和祖先链上的其他类不能包含同样的模块?
我知道非单例类是可以包含同名的模块的,而且可以同时出现在祖先链里。(我用的是ruby1.9.3)
路过懂得求解答,不胜感激。
4类方法的单例类
上面讲的是实例对象的单例类。如果是类的单例类呢?(每一个对象都有单例类,类也是对象,当然也有单例类,类方法就是放在单例类里的。)
单例类不能被继承,但是单例类是可以有父类或者子类的。
如下:
class C
def self.method
p "This is method in C"
end
end
class D < C
end
D.method en = class << C;self;end
class E < en;end;
输出结果
"This is method in C"
can't make subclass of singleton class (TypeError)
结果显示,D.method调用的是C的单例方法,说明D的单例类继承了C的单例类,是它的子类。但是从10-11行,可知,单例类是不能被继承的。
我觉得可以这么认为:在D继承C的时候,D的单例类继承了C的单例类,所以D可以调用C的类方法。同理,D也可以调用Object类的类方法。
整理一下:
类的实例对象的方法查找,先找单例类,然后单例类中的模块。再找父类,父类中的模块。以此类推。
类对象的方法查找,先找单例类(就是类方法),再找父类的单例类,以此类推。
如果找到多个方法,以找到的第一个方法匹配。
ruby中,一个类不能被继承,它也可以有子类。例如ruby中类的单例类。
如果我们用superclass来找父类的话,可得(#代表单例类,假设d是类D的对象,类D继承类C,->表示父类是)
#d->D->C->Object->BasicObject->nil
#D->#C->#Object->#BasicObject->Class->Module->Object->BasicObject->nil
ruby中的方法查找的更多相关文章
- ruby中的可调用对象--方法
上一篇讲了ruby中的可调用对象proc和lambda,他们都是块转换成的对象.ruby中的可调用对象还有方法.通过使用method方法,并且以方法名作为参数(字符串或者符号),就可以得到一个方法对象 ...
- Ruby中方法的设计理念
Ruby中的方法命名遵从与局部变量相同的规则和约定.这是一种设计理念:方法并不因其自身作为方法而被人关注,而是简单地作为提供值的表达式融入到程序的结构中.
- ruby中的可调用对象--proc和lamdba
ruby中将块转变成对象的三种方法 ruby中的大部分东西都是对象,但是块不是.那么,如果你想存下来一个块,方便以后使用,你就需要一个对象.ruby中有三种方法,把块转换成可以利用的对象. Proc. ...
- ruby语法之方法
ruby中的方法相当于python的函数 其定义规则为: 方法名应以小写字母开头.如果您以大写字母作为方法名的开头,Ruby 可能会把它当作常量,从而导致不正确地解析调用. 方法应在调用之前定义,否则 ...
- ruby中的回调方法和钩子方法
在ruby中,当某些特定的事件发生时,将调用回调方法和钩子方法.事件有如下几种: 调用一个不存在的对象方法 类混含一个模块 定义类的子类 给类添加一个实例方法 给对象添加一个单例方法 引用一个不存在的 ...
- 【转】Java中字符串中子串的查找共有四种方法(indexof())
原文网址:http://wfly2004.blog.163.com/blog/static/1176427201032692927349/ Java中字符串中子串的查找共有四种方法,如下:1.int ...
- Java中字符串中子串的查找共有四种方法(indexof())
Java中字符串中子串的查找共有四种方法(indexof()) Java中字符串中子串的查找共有四种方法,如下:1.int indexOf(String str) :返回第一次出现的指定子字符串在此字 ...
- ruby中顶层定义的方法究竟放在哪里?
ruby中顶层(top level)中定义的方法放在main中,证明如下: self.private_methods(false) #IN TOP LEVEL 那么methods方法究竟是在哪定义的, ...
- ruby中如何调用与局部变量同名的私有方法
如果ruby中一个局部变量名和私有方法名同名的话,默认该名称被解释为变量而不是方法: x=10; def x;puts "what?" end 当你输入x实际不能执行x方法.解释器 ...
随机推荐
- 基于struts2框架文件的上传与下载
在开发一些社交网站时,需要有允许用户上传自己本地文件的功能,则需要文件的上传下载代码. 首先考虑的是文件的储存位置,这里不考虑存在数据库,因为通过数据库查询获取十分消耗资源与时间,故需将数据存储在服务 ...
- js math atan2
在双十二活动中,视觉要求实现一个鼠标跟随运动的的效果,就像“觉”的那个效果类似 其实原理很简单,看鼠标从哪个方向进的及从哪个方向出的,然后区块里绝对定位的浮层就可以根据鼠标方向 运动; 如:在鼠标进入 ...
- AuthorizeAttribute示例
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...
- JQuery------制作div模态框
转载: http://blog.csdn.net/li_xiao_ming/article/details/6738922 如图: 代码: html(使用opacity的话content无法变为不透明 ...
- 用MCI处置WAV视频时,怎样才能让视频在当前窗口播放
用MCI处理WAV视频时,怎样才能让视频在当前窗口播放MCI播放视频默认是新开一个窗口播放,播放完毕返回原来的窗口,想着原来窗口播放如何做? mciSendCommand或mciSendString怎 ...
- List<Integer>.remove()的一个小细节
不废话,先上代码: ArrayList<Integer> col = new ArrayList<Integer>(); System.out.println("In ...
- sql语句判断身份证性别等
SELECT t.card_number ,) AS "省份", SUBSTR(t.card_number,,) "出生年月", SUBSTR(t.card_n ...
- js apply()、call() 使用参考
引入,求一个数组的最大值,有这么一种快捷方法:Math.max.apply(null,arr); 但是最初看 JavaScript高级程序设计 的时候,没看懂,原文(斜体表示)如下: 每个函数都包含两 ...
- [Chrome] 如何下载老版本的 Chrome
Google 官方只提供了最新版的 Chrome, 在旧版本的系统(如:Ubuntu 12.04 LTS)安装不上 这里提供了 Chrome 的历史版本下载 备注:Ubuntu 12.04 LTS 可 ...
- 简述泛型、用Maven创建Web项目以及在Web项目上整合SpringMVC
表设计 Timestamp列是否取消"根据当前时间戳自动更新" 是否null及默认值选择合理不合理 外键命名规范及更新和删除时的动作是否合理 泛型 类型参数 --允许在外部指定 ...