本章节,我们来了解下 Ruby 中的 super
关键字,我们会讲述以下内容
- 隐式参数
super
VSsuper()
super
和块super
和祖先链
隐式参数
当一个带参数的方法被其子类之一重写 ( override ) 时,那么可以在子类重写的方法中调用不带参数也不带括号的 super
会自动将子方法的参数传递给父方法。
我们用一个示例来演示下这个机制
class Parent def say(message) puts message end end class Child < Parent def say(message) super end end irb> Child.new.say('Hi!') Hi!
上面的范例中,类 Child
继承自 Parent
类。然后还重写了 Parent
类中的 Parent#say
方法。
在 Child#say
方法内部,我们使用 super
关键字调用父类相应的方法 Parent#say
,并且会把 message
参数自动传递给 Parent#say
方法。
使用 super
关键字,Ruby 会自动在 Child
的祖先链 ancestors chain
中查找 #say
方法,找到了之后就会自动调用该方法并传递 message
作为参数。
如果你对 「 祖先链 」 不熟悉,那么可以访问我们的 Ruby 中的对象模型 ( object model ) ( 上 ) 来了解详情。
但是
我想,你也有同样的疑问 :如果祖先链中的 Parent#say
是一个不带参数的方法,那结果会怎么样 ?
这就涉及到 super
和 super()
相关的知识了。
super VS super()
为了解决上面刚刚提到的问题,我们重新定义 Parent#say
方法,移除 message
参数,就像下面这样
class Parent def say puts "I'm the parent" end end class Child < Parent def say(message) super end end irb> Child.new.say('Hi!') ArgumentError (wrong number of arguments (given 1, expected 0))
运行上面的代码会发现抛出了 ArgumentError (wrong number of arguments (given 1, expected 0))
异常。
因为 Parent#say
方法是一个不带参数的方法,它不需要任何参数,但在 Child#say
方法中的 super
关键字却会将参数 message
隐式的传递给 Parent#say
。
为了避免这个问题,也为了解决这个问题,我们可以明确的指出 super
不需要把子类中相关的参数传递给父类。为此,我们需要在 super
后添加一对小括号 ()
,即 super()
明确的调用父类而不传递任何参数
class Parent def say puts "I'm the parent" end end class Child < Parent def say(message) super() end end irb> Child.new.say('Hi!') I'm the parent
这就是 super
与 super()
的区别,super
会自动将子方法的参数传递给父方法,但 super()
则不会。
super
和块
因为 「 块 」 ( block ) 作为参数如此常见,那么我们要如何在子类中将一个块传递给父类呢 ?
不管三七二十一,我们先修改下代码再说,修改 Parent#say
方法接收一个块作为参数
class Parent def say yield end end class Child < Parent def say super end end irb> Child.new.say { puts 'Hi! Glad to know you Parent!' } Hi! Glad to know you Parent!
哇塞,使用 super
关键字运行正常。
使用 super
关键字会自动将传递给子类方法 Child.new.say
的块传递给父类 Parent#say
方法。
在 Parent#say
方法中,我们使用 yield
关键字捕获一个块,并且执行它。
更多
yield
的知识,可以访问我们的 Ruby 中的 yeild 关键字 ( 上 ) 和 Ruby 中的 yeild 关键字 ( 下 )
super 和祖先链
为了测试更多祖先链的情况,我们修改下 Parent
类,让它继承自 GrandParent
,并在 GrandParent
中也定义一个同样的 say
方法。
然后去掉 Parent
类中的 say
方法
class GrandParent def say(message) puts "GrandParent: #{message}" end end class Parent < GrandParent end class Child < Parent def say(message) super end end irb> Child.new.say('Hi!') GrandParent: Hi!
上面的代码中,Child#say
方法的 super
首先会尝试在 Parent
类中找寻 say
方法,如果找到了就调用该方法,如果没找到,就会继续在祖先链上查找,直到找到为止 GrandParent#say
所以,运行结果就是调用 GrandParent#say
方法输出 GrandParent: Hi!
有关 Ruby 中的 super
关键字知识就这些。