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中的方法查找的更多相关文章

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

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

  2. Ruby中方法的设计理念

    Ruby中的方法命名遵从与局部变量相同的规则和约定.这是一种设计理念:方法并不因其自身作为方法而被人关注,而是简单地作为提供值的表达式融入到程序的结构中.

  3. ruby中的可调用对象--proc和lamdba

    ruby中将块转变成对象的三种方法 ruby中的大部分东西都是对象,但是块不是.那么,如果你想存下来一个块,方便以后使用,你就需要一个对象.ruby中有三种方法,把块转换成可以利用的对象. Proc. ...

  4. ruby语法之方法

    ruby中的方法相当于python的函数 其定义规则为: 方法名应以小写字母开头.如果您以大写字母作为方法名的开头,Ruby 可能会把它当作常量,从而导致不正确地解析调用. 方法应在调用之前定义,否则 ...

  5. ruby中的回调方法和钩子方法

    在ruby中,当某些特定的事件发生时,将调用回调方法和钩子方法.事件有如下几种: 调用一个不存在的对象方法 类混含一个模块 定义类的子类 给类添加一个实例方法 给对象添加一个单例方法 引用一个不存在的 ...

  6. 【转】Java中字符串中子串的查找共有四种方法(indexof())

    原文网址:http://wfly2004.blog.163.com/blog/static/1176427201032692927349/ Java中字符串中子串的查找共有四种方法,如下:1.int ...

  7. Java中字符串中子串的查找共有四种方法(indexof())

    Java中字符串中子串的查找共有四种方法(indexof()) Java中字符串中子串的查找共有四种方法,如下:1.int indexOf(String str) :返回第一次出现的指定子字符串在此字 ...

  8. ruby中顶层定义的方法究竟放在哪里?

    ruby中顶层(top level)中定义的方法放在main中,证明如下: self.private_methods(false) #IN TOP LEVEL 那么methods方法究竟是在哪定义的, ...

  9. ruby中如何调用与局部变量同名的私有方法

    如果ruby中一个局部变量名和私有方法名同名的话,默认该名称被解释为变量而不是方法: x=10; def x;puts "what?" end 当你输入x实际不能执行x方法.解释器 ...

随机推荐

  1. 【mysql】windows7 安装 Mysql

    From: http://jingyan.baidu.com/article/e52e3615a1128c40c70c5174.html 安装(解压) ZIP Archive版是免安装的.只要解压就行 ...

  2. FCN 分割网络详解

    博客来源于:https://www.cnblogs.com/gujianhan/p/6030639.html: https://blog.csdn.net/sinat_24143931/article ...

  3. UVA 548(二叉树重建与遍历)

    J - Tree Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Ap ...

  4. WPS长文档编辑技巧之二:对样式的设置与修改

    目录:       1.使用系统内置样式 2.如何修改样式 3.如何自定义样式 4.在文档使用多级编号 5.结合样式编辑文档大纲 6.利用文档结构图查看大纲结构 正文: 1.使用系统内置样式 在使用样 ...

  5. C++编译遇到参数错误(cannot convert parameter * from 'const char [**]' to 'LPCWSTR')

    转:http://blog.sina.com.cn/s/blog_9ffcd5dc01014nw9.html 前面的几天一直都在复习着被实习落下的C++基础知识.今天在复习着上次创建的窗口程序时,出现 ...

  6. js实现输入框联想搜索

    实现点击和输入搜索联想,把搜索出的列表放到下面的ul列表中,然后再列表中选择并把公司名赋值给输入框,把guid赋值给隐藏域 html <input type="hidden" ...

  7. vue 把后台返回的json拼接成excel并下载

    先封装一下生成excel的方法 downfile.js export default { data() { return {} }, components: {}, created() { }, me ...

  8. 学习ASP.NET MVC3(6)----- Filte

    前言 在开发大项目的时候总会有相关的AOP面向切面编程的组件,而MVC(特指:Asp.Net MVC,以下皆同)项目中不想让MVC开发人员去关心和写类似身份验证,日志,异常,行为截取等这部分重复的代码 ...

  9. SaltStack之Salt-ssh

    上一篇:SaltStack远程执行-返回MySQL 通常salt需要在客户端安装salt-minion使用salt-ssh可以在客户断主机免安装minion 在master主机安装salt-ssh y ...

  10. 后台程序在向tty/串口写数据的时候stop了

    当后台程序向tty/串口写数据的时候stop了. STOPPED(SIGTTOU) .... SIGTTOU:代表的是后台程序向 controlling terminal写数据. 解决办法:暂时在程序 ...