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. 论坛模块__发帖时使用FCKeditor

    论坛模块__发帖时使用FCKeditor 测试 <html> <head> <meta http-equiv="content-type" conte ...

  2. 第三篇:CUDA 标准编程模式

    前言 本文将介绍 CUDA 编程的基本模式,所有 CUDA 程序都基于此模式编写,即使是调用库,库的底层也是这个模式实现的. 模式描述 1. 定义需要在 device 端执行的核函数.( 函数声明前加 ...

  3. RxJava的实现原理

    本周新的一天开始了,让我们一起造一个RxJava,揭秘RxJava的实现原理,  强烈推荐这个

  4. 如何在 Linux 上永久挂载一个 Windows 共享

    导读 如果你已经厌倦了每次重启 Linux 就得重新挂载 Windows 共享,读读这个让共享永久挂载的简单方法. 在 Linux 上和一个 Windows 网络进行交互从来就不是件轻松的事情.想想多 ...

  5. std::unique_lock

    /*与Mutex RAII相关,方便线程上锁,相比std::lock_guard提供了更好的上锁解锁控制,反正我是没看出来也是在构造时上锁,在析构时解锁,感觉和lock_gurad大差不差都是在线程函 ...

  6. 通过python3学习编码

    简介 今天在写python程序的时候,遇到了编码问题,今天,我准备好好了解一下编码问题 ASCII编码 计算机是美国人发明的,最初只有不超过256字符需要编码,1字节能编码2**8个,所以ASCII编 ...

  7. Ubuntu下如何修改文件或者文件夹的权限

    Ubuntu下如何修改文件或者文件夹的权限------chmod的亲身测试   具体原理如下: Linux系统下如何修改文档及文件夹(含子文件夹)权限,我们来看一下.              一 介 ...

  8. Zabbix分布式监控

    上一篇:Zabbix的API的使用 zabbix分布式监控 新建一台主机 安装zabbix proxy和数据库 yum -y install mariadb-server zabbix-proxy-m ...

  9. TCP粘包/拆包 ByteBuf和channel 如果没有Netty? 传统的多线程服务器,这个也是Apache处理请求的模式

    通俗地讲,Netty 能做什么? - 知乎 https://www.zhihu.com/question/24322387 谢邀.netty是一套在java NIO的基础上封装的便于用户开发网络应用程 ...

  10. Chandy-Lamport_algorithm

    Chandy-Lamport algorithm - Wikipedia https://en.m.wikipedia.org/wiki/Chandy-Lamport_algorithm 经典快照算法 ...