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方法.解释器 ...
随机推荐
- BT下载会损害硬盘吗
简而言之,这个问题是否存在,取决于网络带宽的发展速度与硬件性能的发展速度.如果硬件发展的速度快, 网络带宽速度发展慢,那么对大多数人而言,当前的硬件在慢速的带宽下载BT不会造成任何的硬盘损坏. ...
- 根据List<SqlParameter>返回sql条件(where后)
/// <summary> /// 根据参数列表返回sql条件(where后) /// </summary> /// <param name="list&quo ...
- 性能测试工具LoadRunner中进程运行和线程运行区别
loadrunner controller将使用驱动程序mmdrv运行Vuser.用户可以在controller的run-time setting中选择Vuser的运行方式, 是多进程方式or多线程方 ...
- Spring boot Junit Test单元测试
Spring boot 1.40 JUnit 4 需要依赖包 spring-boot-starter-test.spring-test 建立class,加上如下注解,即可进行单元测试,别的帖子里说要加 ...
- ArcGIS ArcMap 与 ArcServer关于Python的冲突
一.问题描述 1.ArcMap 是32位,运行的Python也是32位: 2.ArcGIS Server 是64位,运行的Python是64位: 3.这样就导致注册表和环境变量起冲突,即如果Serve ...
- IT 运行在云端,而云运行在 Linux 上
导读 IT 正在逐渐迁移到云端.那又是什么驱动了云呢?答案是 Linux. 当连微软的 Azure 都开始拥抱 Linux 时,你就应该知道这一切都已经改变了.不管你接不接受, 云正在接管 IT 已经 ...
- Django学习笔记第七篇--实战练习三--关于更有层级的url请求、404错误以及其他响应函数
一.关于更有层级的URL: 可以实现每一个APP一个子URL目录,例如app1的所有操作都在http://www.localhost1.com:5443/app1/xxxx 在工程主文件夹下的工程同名 ...
- 【BZOJ4819】[Sdoi2017]新生舞会 01分数规划+费用流
[BZOJ4819][Sdoi2017]新生舞会 Description 学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴.有n个男生和n个女生参加舞会 买一个男生和一个女 ...
- 常用MySQL函数
MySQL数据库提供了很多函数包括: 数学函数: 字符串函数: 日期和时间函数: 条件判断函数: 系统信息函数: 加密函数: 格式化函数: 一.数学函数 数学函数主要用于处理数字,包括整型.浮点数等. ...
- 面试之四:JVM内存区域分配
1.程序计数器(线程私有)[不会OOM] 记录线程执行的代码位置,每个线程各自独有. 2.栈:虚拟机栈和本地方法栈(线程私有)[会OOM和StackOverflow] 虚拟机栈 每个JAVA方法在执行 ...