本文转自 http://qianjigui.iteye.com/blog/273704
在这里感谢原作者
最近在巩固Ruby 的基本语法知识,在这里把以前一些不知道的特性一一总结下来。
在Ruby中是允许method的重复声明的,并且在合适的时候还允许你去一个一个调用(利用super关键字)。
在这里通过几个实例,我将和大家分享一下Ruby的method查找路径和方式以及super的使用和参数问题。
首先,让大家来看看这样一个例子:
- class A
- def a
- p 'a 1'
- end
- def a
- p 'a 2'
- super
- p 'a 2 END'
- end
- end
- b = A.new
- b.a
输出结果是:
"a 2"
method01.rb:7:in `a': super: no superclass method `a' (NoMethodError)
from method01.rb:13
shell returned 1
从结果中,我们可以发现:如果在一个class里面重复声明两个同样的方法(同样的method名称和参数数目 ),原先的方法将被后面的方法完全覆盖掉(即原先的定义无效了) 。当然这是一个很正常的状况,在Java中语法直接不允许。这里虽然允许了,但是没有什么实际上面的意义。那么这里的super是什么意义呢?让我们慢慢来看。
让我们看看第二个例子:
- class B
- def a
- p 'B a 1'
- end
- end
- class A < B
- def a
- p 'a 1'
- end
- def a
- p 'a 2'
- super
- p 'a 2 END'
- end
- end
- b = A.new
- b.a
输出结果:
"a 2"
"B a 1"
"a 2 END"
似乎很怪异吧,这就是Ruby中super关键字的一部分作用了。其实有点像Java中的Method overwrite 然后在通过super.XXXMethod去调用祖先方法,其中执行的顺序也是类似的。例子中,我们也看到了那个原先定义在A中的a方法确实没有用了。
其实Module也有类似的效果:
- module B
- def a
- p 'B a 1'
- end
- end
- class A
- include B
- def a
- p 'a 1'
- end
- def a
- p 'a 2'
- super
- p 'a 2 END'
- end
- end
- b = A.new
- b.a
输出结果同上面的那个Class-Class的例子:
"a 2"
"B a 1"
"a 2 END"
上面两个的层次关系其实也是method的查找顺序。好像Module和Class有同等效力,那么我们来将两个组合起来。
- class B
- def a
- p 'B a 1'
- end
- end
- module M
- def a
- p 'M a 1'
- super
- p 'M a END'
- end
- end
- class A < B
- include M
- def a
- p 'a 1'
- end
- def a
- p 'a 2'
- super
- p 'a 2 END'
- end
- end
- b = A.new
- b.a
输出结果:
"a 2"
"M a 1"
"B a 1"
"M a END"
"a 2 END"
那么我们可以看到这个的层次结构了:
也即是说,如果同时出现了Module和祖先Class,那么程序将先到Module中寻找。那么如果祖先Class也含有这个Module又会是怎么样的状况呢?
- module M
- def a
- p 'M a 1'
- end
- end
- class B
- def a
- p 'B a 1'
- super
- p 'B a END'
- end
- include M
- end
- class A < B
- include M
- def a
- p 'a 1'
- end
- def a
- p 'a 2'
- super
- p 'a 2 END'
- end
- end
- b = A.new
- b.a
输出结果:
"a 2"
"B a 1"
"M a 1"
"B a END"
"a 2 END"
如果将Module混入它的祖先,那么这个层次结构又发生了变化:
也就是说如果“老子”和“小子”都想有“XXX”,那么这个“XXX”肯定是归“老子”的了,毕竟“小子”得懂得孝道,不光中国是这样,在日本也是这样的文化吧。
下面我们来讨论一下,这个调用super是否把参数也传进去了呢?
在《Ruby For Rails》中有这样一段描述:
以super()调用,则不会传递caller的任何参数;
以super(a,b)调用,则将传递部分参数a、b.
我们仍然以一个例子来看看效果:
- module M
- def a(x=5,y=6)
- p 'M a 1'
- p x
- p y
- end
- end
- class B
- def a(x=3,y=4)
- p 'B a 1'
- p x
- p y
- super(x)
- p 'B a END'
- end
- include M
- end
- class A < B
- include M
- def a
- p 'a 1'
- end
- def a(x=1,y=2)
- p 'a 2'
- p x
- p y
- super
- p 'a 2 END'
- super()
- end
- end
- b = A.new
- b.a(-1,-2)
输出结果:
"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) 时,需要注意由于参数数据不全原方法需要是默认参数值的 。
最后,我给出一个综合的实例吧:
里面有很多错误和冗余,希望大家可以先自己看看输出结果然后再看实际结果,你们会有更多的收获的:
- module M
- def report( a = 4, b =5)
- p "M report begin: a=#{a},b=#{b}"
- a = 6
- super(a)
- p "M report end"
- end
- end
- class B
- def report(a=11,b=12)
- p "B report 1 begin: a=#{a},b=#{b}"
- p "B report 1 end"
- end
- def report(a=13,b=14)
- p "B report 2 begin: a=#{a},b=#{b}"
- #super
- p "B report 2 end"
- end
- end
- class C < B
- def report( a=8,b=9)
- p "C report 1 begin: a=#{a},b=#{b}"
- p "C report 1 end"
- end
- def report( a=7,b=3)
- p "C report 2 begin: a =#{a},b=#{b}"
- super()
- p "C report 2 End"
- end
- include M
- end
- class D < C
- def report( a = 2, b=1)
- p "D report 1 begin: a=#{a},b=#{b}"
- super(a,b)
- p "D report 1 end"
- end
- include M
- def report(a = -2, b=-1)
- p "D report 2 begin: a=#{a},b=#{b}"
- super
- p "D report 2 end"
- end
- end
- d = D.new
- d.report
输出结果:
" 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传递参数有三种(裸词、空、部分)
- 不要写这种很难懂的方法结构