Ruby的函数调用与super

本文转自 http://qianjigui.iteye.com/blog/273704

在这里感谢原作者


最近在巩固Ruby 的基本语法知识,在这里把以前一些不知道的特性一一总结下来。

在Ruby中是允许method的重复声明的,并且在合适的时候还允许你去一个一个调用(利用super关键字)。

在这里通过几个实例,我将和大家分享一下Ruby的method查找路径和方式以及super的使用和参数问题。

首先,让大家来看看这样一个例子:

Ruby代码   收藏代码
  1. class A  
  2.   def a  
  3.     p 'a 1'  
  4.   end  
  5.   def a  
  6.     p 'a 2'  
  7.     super  
  8.     p 'a 2 END'  
  9.   end  
  10. end  
  11.   
  12. b = A.new  
  13. b.a  

 输出结果是:

Output 写道
:!ruby method01.rb
"a 2"
method01.rb:7:in `a': super: no superclass method `a' (NoMethodError)
from method01.rb:13
 

shell returned 1

 从结果中,我们可以发现:如果在一个class里面重复声明两个同样的方法(同样的method名称和参数数目 ),原先的方法将被后面的方法完全覆盖掉(即原先的定义无效了) 。当然这是一个很正常的状况,在Java中语法直接不允许。这里虽然允许了,但是没有什么实际上面的意义。那么这里的super是什么意义呢?让我们慢慢来看。

让我们看看第二个例子:

Ruby代码   收藏代码
  1. class B  
  2.   def a   
  3.     p 'B a 1'  
  4.   end  
  5. end  
  6.   
  7. class A < B  
  8.   def a  
  9.     p 'a 1'  
  10.   end  
  11.   def a  
  12.     p 'a 2'  
  13.     super  
  14.     p 'a 2 END'  
  15.   end  
  16. end  
  17.   
  18. b = A.new  
  19. b.a  

输出结果:

Output 写道
:!ruby method01.rb
"a 2"
"B a 1" 
"a 2 END"

 似乎很怪异吧,这就是Ruby中super关键字的一部分作用了。其实有点像Java中的Method overwrite 然后在通过super.XXXMethod去调用祖先方法,其中执行的顺序也是类似的。例子中,我们也看到了那个原先定义在A中的a方法确实没有用了。

类层次关系 写道
A<B
 

其实Module也有类似的效果:

Ruby代码   收藏代码
  1. module B  
  2.   def a   
  3.     p 'B a 1'  
  4.   end  
  5. end  
  6.   
  7. class A  
  8.   include B  
  9.   def a  
  10.     p 'a 1'  
  11.   end  
  12.   def a  
  13.     p 'a 2'  
  14.     super  
  15.     p 'a 2 END'  
  16.   end  
  17. end  
  18.   
  19. b = A.new  
  20. b.a  

 输出结果同上面的那个Class-Class的例子:

Output 写道
:!ruby method01.rb
"a 2"
"B a 1" 
"a 2 END"
 
类层次关系 写道
A<M

 上面两个的层次关系其实也是method的查找顺序。好像Module和Class有同等效力,那么我们来将两个组合起来。

Ruby代码   收藏代码
  1. class B  
  2.   def a  
  3.     p 'B a 1'  
  4.   end  
  5. end  
  6.   
  7. module M   
  8.   def a   
  9.     p 'M a 1'  
  10.     super  
  11.     p 'M a END'  
  12.   end  
  13. end  
  14.   
  15. class A < B  
  16.   include M  
  17.   def a  
  18.     p 'a 1'  
  19.   end  
  20.   def a  
  21.     p 'a 2'  
  22.     super  
  23.     p 'a 2 END'  
  24.   end  
  25. end  
  26.   
  27. b = A.new  
  28. b.a  

 输出结果:

Output 写道
:!ruby method01.rb
"a 2"
"M a 1" 
"B a 1" 
"M a END" 
"a 2 END"

 那么我们可以看到这个的层次结构了:

类层次结构 写道
A<M<B

 也即是说,如果同时出现了Module和祖先Class,那么程序将先到Module中寻找。那么如果祖先Class也含有这个Module又会是怎么样的状况呢?

Ruby代码   收藏代码
  1. module M   
  2.   def a   
  3.     p 'M a 1'  
  4.   end  
  5. end  
  6.   
  7. class B  
  8.   def a  
  9.     p 'B a 1'  
  10.     super  
  11.     p 'B a END'  
  12.   end  
  13.   include M  
  14. end  
  15.   
  16. class A < B  
  17.   include M  
  18.   def a  
  19.     p 'a 1'  
  20.   end  
  21.   def a  
  22.     p 'a 2'  
  23.     super  
  24.     p 'a 2 END'  
  25.   end  
  26. end  
  27.   
  28. b = A.new  
  29. b.a  

 输出结果:

Output 写道
:!ruby method01.rb
"a 2"
"B a 1" 
"M a 1" 
"B a END" 
"a 2 END"

 如果将Module混入它的祖先,那么这个层次结构又发生了变化:

类层次结构 写道
A<B<M

 也就是说如果“老子”和“小子”都想有“XXX”,那么这个“XXX”肯定是归“老子”的了,毕竟“小子”得懂得孝道,不光中国是这样,在日本也是这样的文化吧。

下面我们来讨论一下,这个调用super是否把参数也传进去了呢?

在《Ruby For Rails》中有这样一段描述:

super的参数传递 写道
以裸词super调用祖先/模块方法(callee),则将传递调用者(caller)的全部方法参数;
以super()调用,则不会传递caller的任何参数;
以super(a,b)调用,则将传递部分参数a、b.

 我们仍然以一个例子来看看效果:

Ruby代码   收藏代码
  1. module M   
  2.   def a(x=5,y=6)   
  3.     p 'M a 1'  
  4.     p x  
  5.     p y  
  6.   end  
  7. end  
  8.   
  9. class B  
  10.   def a(x=3,y=4)  
  11.     p 'B a 1'  
  12.     p x  
  13.     p y  
  14.     super(x)  
  15.     p 'B a END'  
  16.   end  
  17.   include M  
  18. end  
  19.   
  20. class A < B  
  21.   include M  
  22.   def a  
  23.     p 'a 1'  
  24.   end  
  25.   def a(x=1,y=2)  
  26.     p 'a 2'  
  27.     p x  
  28.     p y  
  29.     super  
  30.     p 'a 2 END'  
  31.     super()  
  32.   end  
  33. end  
  34.   
  35. b = A.new  
  36. b.a(-1,-2)  

 输出结果:

Output 写道
:!ruby method01.rb
"a 2"
-1
-2
 
"B a 1"
-1
-2
 
"M a 1"
-1 
6 
"B a END"
"a 2 END"
"B a 1"
3
4
 
"M a 1"
3 
6 
"B a END"

 果然,确实是有不同的效果。不过在使用super() 和super(a) 时,需要注意由于参数数据不全原方法需要是默认参数值的 。

最后,我给出一个综合的实例吧:

 

里面有很多错误和冗余,希望大家可以先自己看看输出结果然后再看实际结果,你们会有更多的收获的:

Ruby代码   收藏代码
  1. module M  
  2.   def report( a = 4, b =5)  
  3.     p "M report begin: a=#{a},b=#{b}"  
  4.     a = 6  
  5.     super(a)  
  6.     p "M report end"  
  7.   end  
  8. end  
  9.   
  10. class B  
  11.   def report(a=11,b=12)  
  12.     p "B report 1 begin: a=#{a},b=#{b}"  
  13.     p "B report 1 end"  
  14.   end  
  15.   def report(a=13,b=14)  
  16.     p "B report 2 begin: a=#{a},b=#{b}"  
  17.     #super  
  18.     p "B report 2 end"  
  19.   end  
  20. end  
  21.   
  22. class C < B  
  23.   def report( a=8,b=9)  
  24.     p "C report 1 begin: a=#{a},b=#{b}"  
  25.     p "C report 1 end"  
  26.   end  
  27.   def report( a=7,b=3)  
  28.     p "C report 2 begin: a =#{a},b=#{b}"  
  29.     super()  
  30.     p "C report 2 End"  
  31.   end  
  32.   include M  
  33. end  
  34.   
  35. class D < C  
  36.   def report( a = 2, b=1)  
  37.     p "D report 1 begin: a=#{a},b=#{b}"  
  38.     super(a,b)  
  39.     p "D report 1 end"  
  40.   end  
  41.   include M  
  42.   def report(a = -2, b=-1)  
  43.     p "D report 2 begin: a=#{a},b=#{b}"  
  44.     super  
  45.     p "D report 2 end"  
  46.   end  
  47. end  
  48.   
  49. d = D.new  
  50. d.report  

 输出结果:

Output 写道
" D report  2 begin:  a=-2,b=-1 "
" C report  2 begin:  a =-2,b=-1 "
" M report begin:  a=4,b=5 "
" B report  2 begin:  a=6,b=14 "
"B report  2 end"
"M report end"
"C report  2 End"
"D report  2 end"

 通过实例,大家应该可以比较清晰的了解method查找调用规律和具体的super使用了。

现在做以下总结:

  • 同一个class和module中,重复声明一个method只有一个有效(用super也无法调用“过时”的几个);
  • 一般过程是:本实例对象的方法体-->本对象对应类的方法体-->本对象包含的module(保证祖先那里没有include)-->祖先-(递归的)->......
  • super传递参数有三种(裸词、空、部分)
  • 不要写这种很难懂的方法结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值