论坛首页 编程语言技术论坛

Ruby的函数调用与super

浏览 2250 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (3) :: 隐藏帖 (7)
作者 正文
   发表时间:2008-11-22  

最近在巩固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

 输出结果是:

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是什么意义呢?让我们慢慢来看。

让我们看看第二个例子:

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

输出结果:

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

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

类层次关系 写道
A<B
 

其实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的例子:

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

 上面两个的层次关系其实也是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

 输出结果:

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又会是怎么样的状况呢?

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

 输出结果:

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.

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

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)

 输出结果:

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) 时,需要注意由于参数数据不全原方法需要是默认参数值的

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

 

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

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

 输出结果:

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传递参数有三种(裸词、空、部分)
  • 不要写这种很难懂的方法结构
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics